1 // Take a look at the license at the top of the repository in the LICENSE file. 2 3 use crate::translate::*; 4 use crate::types::{StaticType, Type}; 5 use std::borrow::Borrow; 6 use std::cmp::Ordering; 7 use std::ffi::{CStr, CString, OsStr}; 8 use std::fmt; 9 use std::hash; 10 use std::ops::Deref; 11 use std::os::raw::{c_char, c_void}; 12 use std::ptr; 13 use std::slice; 14 use std::string::String; 15 16 pub struct GString(Inner); 17 18 enum Inner { 19 Native(Option<CString>), 20 Foreign(ptr::NonNull<c_char>, usize), 21 } 22 23 unsafe impl Send for GString {} 24 unsafe impl Sync for GString {} 25 26 impl GString { 27 /// Create a new GString from a glib-originated string, taking ownership. 28 /// 29 /// # Safety 30 /// 31 /// The provided string must be a valid C string (i.e. `'0'` terminated). 32 /// 33 /// The provided pointer must be allocated by glib such that `g_free(ptr)` 34 /// is valid, as the `GString` will call `g_free(ptr)` on drop. 35 /// 36 /// It is essential that noone else free this pointer as it owned by the 37 /// returned `GString`. 38 /// 39 /// The underlying string must not be mutated, in particular in terms of 40 /// length, underneath the `GString` instance. new(ptr: *mut c_char) -> Self41 unsafe fn new(ptr: *mut c_char) -> Self { 42 assert!(!ptr.is_null()); 43 Self(Inner::Foreign( 44 ptr::NonNull::new_unchecked(ptr), 45 libc::strlen(ptr), 46 )) 47 } 48 49 /// Create a new GString from a glib-originated string, borrowing it rather 50 /// than taking ownership. 51 /// 52 /// # Safety 53 /// 54 /// The provided string must be a valid C string (i.e. `'0'` terminated). 55 /// 56 /// It is essential that noone else free this pointer as it owned by the 57 /// returned `GString` until the borrow is dropped. 58 /// 59 /// The underlying string must not be mutated, in particular in terms of 60 /// length, underneath the `GString` instance for the duration of the borrow. new_borrowed(ptr: *const c_char) -> Borrowed<Self>61 unsafe fn new_borrowed(ptr: *const c_char) -> Borrowed<Self> { 62 assert!(!ptr.is_null()); 63 Borrowed::new(GString(Inner::Foreign( 64 ptr::NonNull::new_unchecked(ptr as *mut _), 65 libc::strlen(ptr), 66 ))) 67 } 68 as_str(&self) -> &str69 pub fn as_str(&self) -> &str { 70 let cstr = match self { 71 GString(Inner::Foreign(ptr, length)) => unsafe { 72 let bytes = slice::from_raw_parts(ptr.as_ptr() as *const u8, length + 1); 73 CStr::from_bytes_with_nul_unchecked(bytes) 74 }, 75 GString(Inner::Native(cstring)) => cstring 76 .as_ref() 77 .expect("Native shouldn't be empty") 78 .as_c_str(), 79 }; 80 cstr.to_str().unwrap() 81 } 82 } 83 84 impl Clone for GString { clone(&self) -> GString85 fn clone(&self) -> GString { 86 let cstring = CString::new(self.as_str().to_string()).expect("CString::new failed"); 87 88 GString(Inner::Native(Some(cstring))) 89 } 90 } 91 92 impl fmt::Debug for GString { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result93 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 94 <&str as fmt::Debug>::fmt(&self.as_str(), f) 95 } 96 } 97 98 impl Drop for GString { drop(&mut self)99 fn drop(&mut self) { 100 if let GString(Inner::Foreign(ptr, _len)) = self { 101 unsafe { 102 ffi::g_free(ptr.as_ptr() as *mut _); 103 } 104 } 105 } 106 } 107 108 impl fmt::Display for GString { fmt(&self, f: &mut fmt::Formatter) -> fmt::Result109 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 110 f.write_str(self.as_str()) 111 } 112 } 113 114 impl hash::Hash for GString { hash<H: hash::Hasher>(&self, state: &mut H)115 fn hash<H: hash::Hasher>(&self, state: &mut H) { 116 let bytes = match self { 117 GString(Inner::Foreign(ptr, length)) => unsafe { 118 slice::from_raw_parts(ptr.as_ptr() as *const u8, length + 1) 119 }, 120 GString(Inner::Native(cstring)) => cstring 121 .as_ref() 122 .expect("Native shouldn't be empty") 123 .as_bytes(), 124 }; 125 state.write(bytes); 126 } 127 } 128 129 impl Borrow<str> for GString { borrow(&self) -> &str130 fn borrow(&self) -> &str { 131 self.as_str() 132 } 133 } 134 135 impl Ord for GString { cmp(&self, other: &GString) -> Ordering136 fn cmp(&self, other: &GString) -> Ordering { 137 self.as_str().cmp(other.as_str()) 138 } 139 } 140 141 impl PartialOrd for GString { partial_cmp(&self, other: &GString) -> Option<Ordering>142 fn partial_cmp(&self, other: &GString) -> Option<Ordering> { 143 Some(self.cmp(other)) 144 } 145 } 146 147 impl PartialEq for GString { eq(&self, other: &GString) -> bool148 fn eq(&self, other: &GString) -> bool { 149 self.as_str() == other.as_str() 150 } 151 } 152 153 impl PartialEq<GString> for String { eq(&self, other: &GString) -> bool154 fn eq(&self, other: &GString) -> bool { 155 self.as_str() == other.as_str() 156 } 157 } 158 159 impl PartialEq<str> for GString { eq(&self, other: &str) -> bool160 fn eq(&self, other: &str) -> bool { 161 self.as_str() == other 162 } 163 } 164 165 impl<'a> PartialEq<&'a str> for GString { eq(&self, other: &&'a str) -> bool166 fn eq(&self, other: &&'a str) -> bool { 167 self.as_str() == *other 168 } 169 } 170 171 impl<'a> PartialEq<GString> for &'a str { eq(&self, other: &GString) -> bool172 fn eq(&self, other: &GString) -> bool { 173 *self == other.as_str() 174 } 175 } 176 177 impl PartialEq<String> for GString { eq(&self, other: &String) -> bool178 fn eq(&self, other: &String) -> bool { 179 self.as_str() == other.as_str() 180 } 181 } 182 183 impl PartialEq<GString> for str { eq(&self, other: &GString) -> bool184 fn eq(&self, other: &GString) -> bool { 185 self == other.as_str() 186 } 187 } 188 189 impl PartialOrd<GString> for String { partial_cmp(&self, other: &GString) -> Option<Ordering>190 fn partial_cmp(&self, other: &GString) -> Option<Ordering> { 191 Some(self.cmp(&String::from(other.as_str()))) 192 } 193 } 194 195 impl PartialOrd<String> for GString { partial_cmp(&self, other: &String) -> Option<Ordering>196 fn partial_cmp(&self, other: &String) -> Option<Ordering> { 197 Some(self.as_str().cmp(other.as_str())) 198 } 199 } 200 201 impl PartialOrd<GString> for str { partial_cmp(&self, other: &GString) -> Option<Ordering>202 fn partial_cmp(&self, other: &GString) -> Option<Ordering> { 203 Some(self.cmp(other)) 204 } 205 } 206 207 impl PartialOrd<str> for GString { partial_cmp(&self, other: &str) -> Option<Ordering>208 fn partial_cmp(&self, other: &str) -> Option<Ordering> { 209 Some(self.as_str().cmp(other)) 210 } 211 } 212 213 impl Eq for GString {} 214 215 impl AsRef<str> for GString { as_ref(&self) -> &str216 fn as_ref(&self) -> &str { 217 self.as_str() 218 } 219 } 220 221 impl AsRef<OsStr> for GString { as_ref(&self) -> &OsStr222 fn as_ref(&self) -> &OsStr { 223 OsStr::new(self.as_str()) 224 } 225 } 226 227 impl Deref for GString { 228 type Target = str; 229 deref(&self) -> &str230 fn deref(&self) -> &str { 231 self.as_str() 232 } 233 } 234 235 impl From<GString> for String { 236 #[inline] from(mut s: GString) -> Self237 fn from(mut s: GString) -> Self { 238 if let GString(Inner::Native(ref mut cstring)) = s { 239 if let Ok(s) = cstring 240 .take() 241 .expect("Native shouldn't be empty") 242 .into_string() 243 { 244 return s; 245 } 246 } 247 Self::from(s.as_str()) 248 } 249 } 250 251 impl From<GString> for Box<str> { 252 #[inline] from(s: GString) -> Self253 fn from(s: GString) -> Self { 254 let st: String = s.into(); 255 st.into_boxed_str() 256 } 257 } 258 259 impl From<String> for GString { 260 #[inline] from(s: String) -> Self261 fn from(s: String) -> Self { 262 s.into_bytes().into() 263 } 264 } 265 266 impl From<Box<str>> for GString { 267 #[inline] from(s: Box<str>) -> Self268 fn from(s: Box<str>) -> Self { 269 s.as_bytes().to_vec().into() 270 } 271 } 272 273 impl<'a> From<&'a str> for GString { 274 #[inline] from(s: &'a str) -> Self275 fn from(s: &'a str) -> Self { 276 s.as_bytes().to_vec().into() 277 } 278 } 279 280 impl From<Vec<u8>> for GString { 281 #[inline] from(s: Vec<u8>) -> Self282 fn from(s: Vec<u8>) -> Self { 283 let cstring = CString::new(s).expect("CString::new failed"); 284 cstring.into() 285 } 286 } 287 288 impl From<CString> for GString { 289 #[inline] from(s: CString) -> Self290 fn from(s: CString) -> Self { 291 Self(Inner::Native(Some(s))) 292 } 293 } 294 295 impl<'a> From<&'a CStr> for GString { 296 #[inline] from(c: &'a CStr) -> Self297 fn from(c: &'a CStr) -> Self { 298 CString::from(c).into() 299 } 300 } 301 302 #[doc(hidden)] 303 impl FromGlibPtrFull<*const c_char> for GString { 304 #[inline] from_glib_full(ptr: *const c_char) -> Self305 unsafe fn from_glib_full(ptr: *const c_char) -> Self { 306 Self::new(ptr as *mut _) 307 } 308 } 309 310 #[doc(hidden)] 311 impl FromGlibPtrFull<*mut u8> for GString { 312 #[inline] from_glib_full(ptr: *mut u8) -> Self313 unsafe fn from_glib_full(ptr: *mut u8) -> Self { 314 Self::new(ptr as *mut _) 315 } 316 } 317 318 #[doc(hidden)] 319 impl FromGlibPtrFull<*mut i8> for GString { 320 #[inline] from_glib_full(ptr: *mut i8) -> Self321 unsafe fn from_glib_full(ptr: *mut i8) -> Self { 322 Self::new(ptr as *mut _) 323 } 324 } 325 326 #[doc(hidden)] 327 impl FromGlibPtrNone<*const c_char> for GString { 328 #[inline] from_glib_none(ptr: *const c_char) -> Self329 unsafe fn from_glib_none(ptr: *const c_char) -> Self { 330 assert!(!ptr.is_null()); 331 let cstr = CStr::from_ptr(ptr); 332 cstr.into() 333 } 334 } 335 336 #[doc(hidden)] 337 impl FromGlibPtrNone<*mut u8> for GString { 338 #[inline] from_glib_none(ptr: *mut u8) -> Self339 unsafe fn from_glib_none(ptr: *mut u8) -> Self { 340 assert!(!ptr.is_null()); 341 let cstr = CStr::from_ptr(ptr as *mut _); 342 cstr.into() 343 } 344 } 345 346 #[doc(hidden)] 347 impl FromGlibPtrNone<*mut i8> for GString { 348 #[inline] from_glib_none(ptr: *mut i8) -> Self349 unsafe fn from_glib_none(ptr: *mut i8) -> Self { 350 assert!(!ptr.is_null()); 351 let cstr = CStr::from_ptr(ptr as *mut _); 352 cstr.into() 353 } 354 } 355 356 #[doc(hidden)] 357 impl FromGlibPtrBorrow<*const c_char> for GString { 358 #[inline] from_glib_borrow(ptr: *const c_char) -> Borrowed<Self>359 unsafe fn from_glib_borrow(ptr: *const c_char) -> Borrowed<Self> { 360 Self::new_borrowed(ptr) 361 } 362 } 363 364 #[doc(hidden)] 365 impl FromGlibPtrBorrow<*mut u8> for GString { 366 #[inline] from_glib_borrow(ptr: *mut u8) -> Borrowed<Self>367 unsafe fn from_glib_borrow(ptr: *mut u8) -> Borrowed<Self> { 368 Self::new_borrowed(ptr as *const c_char) 369 } 370 } 371 372 #[doc(hidden)] 373 impl FromGlibPtrBorrow<*mut i8> for GString { 374 #[inline] from_glib_borrow(ptr: *mut i8) -> Borrowed<Self>375 unsafe fn from_glib_borrow(ptr: *mut i8) -> Borrowed<Self> { 376 Self::new_borrowed(ptr as *const c_char) 377 } 378 } 379 380 #[doc(hidden)] 381 impl<'a> ToGlibPtr<'a, *const c_char> for GString { 382 type Storage = &'a Self; 383 384 #[inline] to_glib_none(&'a self) -> Stash<'a, *const c_char, Self>385 fn to_glib_none(&'a self) -> Stash<'a, *const c_char, Self> { 386 Stash(self.as_ptr() as *const _, self) 387 } 388 389 #[inline] to_glib_full(&self) -> *const c_char390 fn to_glib_full(&self) -> *const c_char { 391 unsafe { 392 ffi::g_strndup(self.as_ptr() as *const c_char, self.len() as libc::size_t) 393 as *const c_char 394 } 395 } 396 } 397 398 #[doc(hidden)] 399 impl<'a> ToGlibPtr<'a, *mut c_char> for GString { 400 type Storage = &'a Self; 401 402 #[inline] to_glib_none(&'a self) -> Stash<'a, *mut c_char, Self>403 fn to_glib_none(&'a self) -> Stash<'a, *mut c_char, Self> { 404 Stash(self.as_ptr() as *mut _, self) 405 } 406 407 #[inline] to_glib_full(&self) -> *mut c_char408 fn to_glib_full(&self) -> *mut c_char { 409 unsafe { 410 ffi::g_strndup(self.as_ptr() as *const c_char, self.len() as libc::size_t) 411 as *mut c_char 412 } 413 } 414 } 415 416 #[doc(hidden)] 417 impl<'a> FromGlibContainer<*const c_char, *const i8> for GString { from_glib_none_num(ptr: *const i8, num: usize) -> Self418 unsafe fn from_glib_none_num(ptr: *const i8, num: usize) -> Self { 419 if num == 0 || ptr.is_null() { 420 return Self::from(""); 421 } 422 let mut bytes = Vec::with_capacity(num + 1); 423 let slice = slice::from_raw_parts(ptr as *const u8, num); 424 bytes.extend_from_slice(slice); 425 bytes.push(0); 426 427 CStr::from_bytes_with_nul_unchecked(bytes.as_slice()).into() 428 } 429 from_glib_container_num(ptr: *const i8, num: usize) -> Self430 unsafe fn from_glib_container_num(ptr: *const i8, num: usize) -> Self { 431 if num == 0 || ptr.is_null() { 432 return Self::from(""); 433 } 434 GString(Inner::Foreign( 435 ptr::NonNull::new_unchecked(ptr as *mut _), 436 num, 437 )) 438 } 439 from_glib_full_num(ptr: *const i8, num: usize) -> Self440 unsafe fn from_glib_full_num(ptr: *const i8, num: usize) -> Self { 441 if num == 0 || ptr.is_null() { 442 return Self::from(""); 443 } 444 GString(Inner::Foreign( 445 ptr::NonNull::new_unchecked(ptr as *mut _), 446 num, 447 )) 448 } 449 } 450 451 #[doc(hidden)] 452 impl<'a> FromGlibContainer<*const c_char, *mut i8> for GString { from_glib_none_num(ptr: *mut i8, num: usize) -> Self453 unsafe fn from_glib_none_num(ptr: *mut i8, num: usize) -> Self { 454 FromGlibContainer::from_glib_none_num(ptr as *const i8, num) 455 } 456 from_glib_container_num(ptr: *mut i8, num: usize) -> Self457 unsafe fn from_glib_container_num(ptr: *mut i8, num: usize) -> Self { 458 FromGlibContainer::from_glib_container_num(ptr as *const i8, num) 459 } 460 from_glib_full_num(ptr: *mut i8, num: usize) -> Self461 unsafe fn from_glib_full_num(ptr: *mut i8, num: usize) -> Self { 462 FromGlibContainer::from_glib_full_num(ptr as *const i8, num) 463 } 464 } 465 466 #[doc(hidden)] 467 impl<'a> FromGlibContainer<*const c_char, *const u8> for GString { from_glib_none_num(ptr: *const u8, num: usize) -> Self468 unsafe fn from_glib_none_num(ptr: *const u8, num: usize) -> Self { 469 FromGlibContainer::from_glib_none_num(ptr as *const i8, num) 470 } 471 from_glib_container_num(ptr: *const u8, num: usize) -> Self472 unsafe fn from_glib_container_num(ptr: *const u8, num: usize) -> Self { 473 FromGlibContainer::from_glib_container_num(ptr as *const i8, num) 474 } 475 from_glib_full_num(ptr: *const u8, num: usize) -> Self476 unsafe fn from_glib_full_num(ptr: *const u8, num: usize) -> Self { 477 FromGlibContainer::from_glib_full_num(ptr as *const i8, num) 478 } 479 } 480 481 #[doc(hidden)] 482 impl<'a> FromGlibContainer<*const c_char, *mut u8> for GString { from_glib_none_num(ptr: *mut u8, num: usize) -> Self483 unsafe fn from_glib_none_num(ptr: *mut u8, num: usize) -> Self { 484 FromGlibContainer::from_glib_none_num(ptr as *const i8, num) 485 } 486 from_glib_container_num(ptr: *mut u8, num: usize) -> Self487 unsafe fn from_glib_container_num(ptr: *mut u8, num: usize) -> Self { 488 FromGlibContainer::from_glib_container_num(ptr as *const i8, num) 489 } 490 from_glib_full_num(ptr: *mut u8, num: usize) -> Self491 unsafe fn from_glib_full_num(ptr: *mut u8, num: usize) -> Self { 492 FromGlibContainer::from_glib_full_num(ptr as *const i8, num) 493 } 494 } 495 496 impl GlibPtrDefault for GString { 497 type GlibType = *const c_char; 498 } 499 500 impl StaticType for GString { static_type() -> Type501 fn static_type() -> Type { 502 String::static_type() 503 } 504 } 505 506 impl crate::value::ValueType for GString { 507 type Type = String; 508 } 509 510 unsafe impl<'a> crate::value::FromValue<'a> for GString { 511 type Checker = crate::value::GenericValueTypeOrNoneChecker<Self>; 512 from_value(value: &'a crate::Value) -> Self513 unsafe fn from_value(value: &'a crate::Value) -> Self { 514 Self::from(<&str>::from_value(value)) 515 } 516 } 517 518 impl crate::value::ToValue for GString { to_value(&self) -> crate::Value519 fn to_value(&self) -> crate::Value { 520 <&str>::to_value(&self.as_str()) 521 } 522 value_type(&self) -> Type523 fn value_type(&self) -> Type { 524 String::static_type() 525 } 526 } 527 528 impl crate::value::ToValueOptional for GString { to_value_optional(s: Option<&Self>) -> crate::Value529 fn to_value_optional(s: Option<&Self>) -> crate::Value { 530 <str>::to_value_optional(s.as_ref().map(|s| s.as_str())) 531 } 532 } 533 534 impl StaticType for Vec<GString> { static_type() -> Type535 fn static_type() -> Type { 536 <Vec<String>>::static_type() 537 } 538 } 539 540 impl crate::value::ValueType for Vec<GString> { 541 type Type = Vec<GString>; 542 } 543 544 unsafe impl<'a> crate::value::FromValue<'a> for Vec<GString> { 545 type Checker = crate::value::GenericValueTypeChecker<Self>; 546 from_value(value: &'a crate::value::Value) -> Self547 unsafe fn from_value(value: &'a crate::value::Value) -> Self { 548 let ptr = gobject_ffi::g_value_get_boxed(value.to_glib_none().0) as *const *const c_char; 549 FromGlibPtrContainer::from_glib_none(ptr) 550 } 551 } 552 553 impl crate::value::ToValue for Vec<GString> { to_value(&self) -> crate::value::Value554 fn to_value(&self) -> crate::value::Value { 555 unsafe { 556 let mut value = crate::value::Value::for_value_type::<Self>(); 557 let ptr: *mut *mut c_char = self.to_glib_full(); 558 gobject_ffi::g_value_take_boxed(value.to_glib_none_mut().0, ptr as *const c_void); 559 value 560 } 561 } 562 value_type(&self) -> Type563 fn value_type(&self) -> Type { 564 <Vec<GString>>::static_type() 565 } 566 } 567 568 impl_from_glib_container_as_vec_string!(GString, *const c_char); 569 impl_from_glib_container_as_vec_string!(GString, *mut c_char); 570 571 #[cfg(test)] 572 #[allow(clippy::blacklisted_name)] 573 mod tests { 574 use super::{FromGlibContainer, GString}; 575 use std::ffi::CString; 576 577 #[test] test_gstring()578 fn test_gstring() { 579 let data = CString::new("foo").unwrap(); 580 let ptr = data.as_ptr(); 581 582 unsafe { 583 let ptr_copy = ffi::g_strdup(ptr); 584 let gstring = GString::new(ptr_copy); 585 assert_eq!(gstring.as_str(), "foo"); 586 let foo: Box<str> = gstring.into(); 587 assert_eq!(foo.as_ref(), "foo"); 588 } 589 } 590 591 #[test] test_owned_glib_string()592 fn test_owned_glib_string() { 593 let data = CString::new("foo").unwrap(); 594 let ptr = data.as_ptr(); 595 unsafe { 596 let ptr_copy = ffi::g_strdup(ptr); 597 let gstr = GString::new(ptr_copy); 598 assert_eq!(gstr, "foo"); 599 } 600 } 601 602 #[test] test_gstring_from_str()603 fn test_gstring_from_str() { 604 let gstring: GString = "foo".into(); 605 assert_eq!(gstring.as_str(), "foo"); 606 let foo: Box<str> = gstring.into(); 607 assert_eq!(foo.as_ref(), "foo"); 608 } 609 610 #[test] test_gstring_from_cstring()611 fn test_gstring_from_cstring() { 612 let cstr = CString::new("foo").unwrap(); 613 let gstring = GString::from(cstr); 614 assert_eq!(gstring.as_str(), "foo"); 615 let foo: Box<str> = gstring.into(); 616 assert_eq!(foo.as_ref(), "foo"); 617 } 618 619 #[test] test_string_from_gstring()620 fn test_string_from_gstring() { 621 let cstr = CString::new("foo").unwrap(); 622 let gstring = GString::from(cstr); 623 assert_eq!(gstring.as_str(), "foo"); 624 let s = String::from(gstring); 625 assert_eq!(s, "foo"); 626 } 627 628 #[test] test_vec_u8_to_gstring()629 fn test_vec_u8_to_gstring() { 630 let v: &[u8] = b"foo"; 631 let s: GString = Vec::from(v).into(); 632 assert_eq!(s.as_str(), "foo"); 633 } 634 635 #[test] test_from_glib_container()636 fn test_from_glib_container() { 637 unsafe { 638 let test_a: GString = FromGlibContainer::from_glib_container_num( 639 ffi::g_strdup("hello_world".as_ptr() as *const _), 640 5, 641 ); 642 assert_eq!("hello", test_a.as_str()); 643 644 let test_b: GString = FromGlibContainer::from_glib_none_num("hello_world".as_ptr(), 5); 645 assert_eq!("hello", test_b.as_str()); 646 647 let test_c: GString = 648 FromGlibContainer::from_glib_none_num(std::ptr::null::<std::os::raw::c_char>(), 0); 649 assert_eq!("", test_c.as_str()); 650 651 let test_d: GString = FromGlibContainer::from_glib_none_num("".as_ptr(), 0); 652 assert_eq!("", test_d.as_str()); 653 654 let test_e: GString = 655 FromGlibContainer::from_glib_container_num(ffi::g_strdup(std::ptr::null()), 0); 656 assert_eq!("", test_e.as_str()); 657 } 658 } 659 660 #[test] test_hashmap()661 fn test_hashmap() { 662 use std::collections::HashMap; 663 664 let cstr = CString::new("foo").unwrap(); 665 let gstring = GString::from(cstr); 666 assert_eq!(gstring.as_str(), "foo"); 667 let mut h: HashMap<GString, i32> = HashMap::new(); 668 h.insert(gstring, 42); 669 let gstring: GString = "foo".into(); 670 assert!(h.contains_key(&gstring)); 671 } 672 } 673