From e638de61e9c242748f7426f4dc4b896b283b1c97 Mon Sep 17 00:00:00 2001 From: XX Date: Sat, 9 Jul 2022 16:16:24 +0300 Subject: [PATCH 1/3] Impl str Borrow for IString --- src/string.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/string.rs b/src/string.rs index e55b7d7..f97b5be 100644 --- a/src/string.rs +++ b/src/string.rs @@ -98,6 +98,12 @@ impl AsRef for IString { } } +impl std::borrow::Borrow for IString { + fn borrow(&self) -> &str { + &*self + } +} + #[cfg(test)] mod test_string { use super::*; From 396244256c6c385217c3792cc32a87056c017cd6 Mon Sep 17 00:00:00 2001 From: XX Date: Sat, 9 Jul 2022 16:38:08 +0300 Subject: [PATCH 2/3] Impl Hash as inner str hash --- src/string.rs | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/string.rs b/src/string.rs index f97b5be..a4fe1d4 100644 --- a/src/string.rs +++ b/src/string.rs @@ -2,7 +2,7 @@ /// /// This type is cheap to clone and thus implements [`ImplicitClone`]. It can be created based on a /// `&'static str` or based on a reference counted string slice ([`str`]). -#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, PartialEq, Eq)] pub enum IString { /// A static string slice. Static(&'static str), @@ -98,6 +98,13 @@ impl AsRef for IString { } } +impl std::hash::Hash for IString { + #[inline] + fn hash(&self, state: &mut H) { + std::hash::Hash::hash(self.as_ref(), state) + } +} + impl std::borrow::Borrow for IString { fn borrow(&self) -> &str { &*self @@ -124,4 +131,15 @@ mod test_string { fn deref_str() { assert_eq!(IString::Static("foo").to_uppercase(), "FOO"); } + + #[test] + fn borrow_str() { + let map: std::collections::HashMap<_, _> = [ + (IString::Static("foo"), true), + (IString::Rc(Rc::from("bar")), true) + ].into_iter().collect(); + + assert_eq!(map.get("foo").copied(), Some(true)); + assert_eq!(map.get("bar").copied(), Some(true)); + } } From 4998b78640a2652bed14b6a263baa66c770d93cf Mon Sep 17 00:00:00 2001 From: XX Date: Tue, 12 Jul 2022 12:55:57 +0300 Subject: [PATCH 3/3] Impl PartialEq manually and add as_str method --- src/string.rs | 78 +++++++++++++++++++++++++++++++++------------------ 1 file changed, 50 insertions(+), 28 deletions(-) diff --git a/src/string.rs b/src/string.rs index a4fe1d4..4474a8c 100644 --- a/src/string.rs +++ b/src/string.rs @@ -1,8 +1,10 @@ +use std::fmt::Debug; + /// An immutable string type inspired by [Immutable.js](https://immutable-js.com/). /// /// This type is cheap to clone and thus implements [`ImplicitClone`]. It can be created based on a /// `&'static str` or based on a reference counted string slice ([`str`]). -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, Eq)] pub enum IString { /// A static string slice. Static(&'static str), @@ -10,6 +12,27 @@ pub enum IString { Rc(Rc), } +impl IString { + /// Extracts a string slice containing the entire `IString`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use implicit_clone::unsync::IString; + /// let s = IString::from("foo"); + /// + /// assert_eq!("foo", s.as_str()); + /// ``` + pub fn as_str(&self) -> &str { + match self { + Self::Static(s) => *s, + Self::Rc(s) => &*s, + } + } +} + impl Default for IString { fn default() -> Self { Self::Static("") @@ -20,10 +43,7 @@ impl ImplicitClone for IString {} impl fmt::Display for IString { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - Self::Static(s) => s.fmt(f), - Self::Rc(s) => s.fmt(f), - } + fmt::Display::fmt(self.as_str(), f) } } @@ -45,39 +65,33 @@ impl From> for IString { } } +impl PartialEq for IString { + fn eq(&self, other: &IString) -> bool { + self.as_str().eq(other.as_str()) + } +} + impl PartialEq for IString { fn eq(&self, other: &str) -> bool { - match self { - Self::Static(s) => s.eq(&other), - Self::Rc(s) => (**s).eq(other), - } + self.as_str().eq(other) } } impl PartialEq<&str> for IString { fn eq(&self, other: &&str) -> bool { - match self { - Self::Static(s) => s.eq(other), - Self::Rc(s) => (**s).eq(*other), - } + self.eq(*other) } } impl PartialEq for IString { fn eq(&self, other: &String) -> bool { - match self { - Self::Static(s) => s.eq(&other), - Self::Rc(s) => (**s).eq(other), - } + self.eq(other.as_str()) } } impl PartialEq<&String> for IString { fn eq(&self, other: &&String) -> bool { - match self { - Self::Static(s) => s.eq(other), - Self::Rc(s) => (**s).eq(*other), - } + self.eq(*other) } } @@ -85,29 +99,26 @@ impl std::ops::Deref for IString { type Target = str; fn deref(&self) -> &Self::Target { - match self { - Self::Static(s) => *s, - Self::Rc(s) => &*s, - } + self.as_str() } } impl AsRef for IString { fn as_ref(&self) -> &str { - &*self + self.as_str() } } impl std::hash::Hash for IString { #[inline] fn hash(&self, state: &mut H) { - std::hash::Hash::hash(self.as_ref(), state) + std::hash::Hash::hash(self.as_str(), state) } } impl std::borrow::Borrow for IString { fn borrow(&self) -> &str { - &*self + self.as_str() } } @@ -115,6 +126,17 @@ impl std::borrow::Borrow for IString { mod test_string { use super::*; + #[test] + fn cmp() { + assert_eq!(IString::Static("foo"), IString::Static("foo")); + assert_eq!(IString::Static("foo"), IString::Rc(Rc::from("foo"))); + assert_eq!(IString::Rc(Rc::from("foo")), IString::Rc(Rc::from("foo"))); + + assert_ne!(IString::Static("foo"), IString::Static("bar")); + assert_ne!(IString::Static("foo"), IString::Rc(Rc::from("bar"))); + assert_ne!(IString::Rc(Rc::from("foo")), IString::Rc(Rc::from("bar"))); + } + #[test] fn string_cmp() { assert_eq!(IString::from("foo"), "foo");