From 11603de25f97d1a08692242a41f017d8e09e611d Mon Sep 17 00:00:00 2001 From: Alex Martens Date: Sat, 21 Oct 2023 02:14:08 -0700 Subject: [PATCH] string: replace From<&str> with TryFrom<&str> --- CHANGELOG.md | 2 +- src/string.rs | 116 +++++++++++++++++++++++++++++--------------------- 2 files changed, 69 insertions(+), 49 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 108e3ab9fb..32753b771c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,9 +31,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - export `IndexSet` and `IndexMap` iterator types. - [breaking-change] export `IndexMapKeys`, `IndexMapValues` and `IndexMapValuesMut` iterator types. - - [breaking-change] this crate now uses `portable-atomic` v1.0 instead of `atomic-polyfill` for emulating CAS instructions on targets where they're not natively available. +- [breaking-change] `From<&str>` for `String` was replaced with `TryFrom<&str>` because the `From` trait must not fail. ### Fixed diff --git a/src/string.rs b/src/string.rs index 649597ed29..a40cbb40b2 100644 --- a/src/string.rs +++ b/src/string.rs @@ -39,11 +39,12 @@ impl String { /// ``` /// use heapless::String; /// - /// let s: String<4> = String::from("ab"); + /// let s: String<4> = String::try_from("ab")?; /// let b = s.into_bytes(); /// assert!(b.len() == 2); /// /// assert_eq!(&['a' as u8, 'b' as u8], &b[..]); + /// # Ok::<(), ()>(()) /// ``` #[inline] pub fn into_bytes(self) -> Vec { @@ -59,11 +60,12 @@ impl String { /// ``` /// use heapless::String; /// - /// let mut s: String<4> = String::from("ab"); + /// let mut s: String<4> = String::try_from("ab")?; /// assert!(s.as_str() == "ab"); /// /// let _s = s.as_str(); /// // s.push('c'); // <- cannot borrow `s` as mutable because it is also borrowed as immutable + /// # Ok::<(), ()>(()) /// ``` #[inline] pub fn as_str(&self) -> &str { @@ -79,9 +81,10 @@ impl String { /// ``` /// use heapless::String; /// - /// let mut s: String<4> = String::from("ab"); + /// let mut s: String<4> = String::try_from("ab")?; /// let s = s.as_mut_str(); /// s.make_ascii_uppercase(); + /// # Ok::<(), ()>(()) /// ``` #[inline] pub fn as_mut_str(&mut self) -> &mut str { @@ -102,7 +105,9 @@ impl String { /// Basic usage: /// /// ``` - /// let mut s = String::from("hello"); + /// use heapless::String; + /// + /// let mut s: String<8> = String::try_from("hello")?; /// /// unsafe { /// let vec = s.as_mut_vec(); @@ -111,6 +116,7 @@ impl String { /// vec.reverse(); /// } /// assert_eq!(s, "olleh"); + /// # Ok::<(), ()>(()) /// ``` pub unsafe fn as_mut_vec(&mut self) -> &mut Vec { &mut self.vec @@ -125,13 +131,14 @@ impl String { /// ``` /// use heapless::String; /// - /// let mut s: String<8> = String::from("foo"); + /// let mut s: String<8> = String::try_from("foo")?; /// /// assert!(s.push_str("bar").is_ok()); /// /// assert_eq!("foobar", s); /// /// assert!(s.push_str("tender").is_err()); + /// # Ok::<(), ()>(()) /// ``` #[inline] pub fn push_str(&mut self, string: &str) -> Result<(), ()> { @@ -166,7 +173,7 @@ impl String { /// ``` /// use heapless::String; /// - /// let mut s: String<8> = String::from("abc"); + /// let mut s: String<8> = String::try_from("abc")?; /// /// s.push('1').unwrap(); /// s.push('2').unwrap(); @@ -175,6 +182,7 @@ impl String { /// assert!("abc123" == s.as_str()); /// /// assert_eq!("abc123", s); + /// # Ok::<(), ()>(()) /// ``` #[inline] pub fn push(&mut self, c: char) -> Result<(), ()> { @@ -207,11 +215,12 @@ impl String { /// ``` /// use heapless::String; /// - /// let mut s: String<8> = String::from("hello"); + /// let mut s: String<8> = String::try_from("hello")?; /// /// s.truncate(2); /// /// assert_eq!("he", s); + /// # Ok::<(), ()>(()) /// ``` #[inline] pub fn truncate(&mut self, new_len: usize) { @@ -234,13 +243,14 @@ impl String { /// ``` /// use heapless::String; /// - /// let mut s: String<8> = String::from("foo"); + /// let mut s: String<8> = String::try_from("foo")?; /// /// assert_eq!(s.pop(), Some('o')); /// assert_eq!(s.pop(), Some('o')); /// assert_eq!(s.pop(), Some('f')); /// /// assert_eq!(s.pop(), None); + /// Ok::<(), ()>(()) /// ``` pub fn pop(&mut self) -> Option { let ch = self.chars().rev().next()?; @@ -267,13 +277,14 @@ impl String { /// ``` /// use heapless::String; /// - /// let mut s: String<8> = String::from("foo"); + /// let mut s: String<8> = String::try_from("foo")?; /// /// s.clear(); /// /// assert!(s.is_empty()); /// assert_eq!(0, s.len()); /// assert_eq!(8, s.capacity()); + /// Ok::<(), ()>(()) /// ``` #[inline] pub fn clear(&mut self) { @@ -287,11 +298,12 @@ impl Default for String { } } -impl<'a, const N: usize> From<&'a str> for String { - fn from(s: &'a str) -> Self { +impl<'a, const N: usize> TryFrom<&'a str> for String { + type Error = (); + fn try_from(s: &'a str) -> Result { let mut new = String::new(); - new.push_str(s).unwrap(); - new + new.push_str(s)?; + Ok(new) } } @@ -474,31 +486,33 @@ impl Ord for String { } } -macro_rules! impl_from_num { +macro_rules! impl_try_from_num { ($num:ty, $size:expr) => { - impl From<$num> for String { - fn from(s: $num) -> Self { + impl core::convert::TryFrom<$num> for String { + type Error = (); + fn try_from(s: $num) -> Result { let mut new = String::new(); - write!(&mut new, "{}", s).unwrap(); - new + write!(&mut new, "{}", s).map_err(|_| ())?; + Ok(new) } } }; } -impl_from_num!(i8, 4); -impl_from_num!(i16, 6); -impl_from_num!(i32, 11); -impl_from_num!(i64, 20); +impl_try_from_num!(i8, 4); +impl_try_from_num!(i16, 6); +impl_try_from_num!(i32, 11); +impl_try_from_num!(i64, 20); -impl_from_num!(u8, 3); -impl_from_num!(u16, 5); -impl_from_num!(u32, 10); -impl_from_num!(u64, 20); +impl_try_from_num!(u8, 3); +impl_try_from_num!(u16, 5); +impl_try_from_num!(u32, 10); +impl_try_from_num!(u64, 20); #[cfg(test)] mod tests { use crate::{String, Vec}; + use core::convert::TryFrom; #[test] fn static_new() { @@ -507,7 +521,7 @@ mod tests { #[test] fn clone() { - let s1: String<20> = String::from("abcd"); + let s1: String<20> = String::try_from("abcd").unwrap(); let mut s2 = s1.clone(); s2.push_str(" efgh").unwrap(); @@ -517,16 +531,16 @@ mod tests { #[test] fn cmp() { - let s1: String<4> = String::from("abcd"); - let s2: String<4> = String::from("zzzz"); + let s1: String<4> = String::try_from("abcd").unwrap(); + let s2: String<4> = String::try_from("zzzz").unwrap(); assert!(s1 < s2); } #[test] fn cmp_heterogenous_size() { - let s1: String<4> = String::from("abcd"); - let s2: String<8> = String::from("zzzz"); + let s1: String<4> = String::try_from("abcd").unwrap(); + let s2: String<8> = String::try_from("zzzz").unwrap(); assert!(s1 < s2); } @@ -535,7 +549,7 @@ mod tests { fn debug() { use core::fmt::Write; - let s: String<8> = String::from("abcd"); + let s: String<8> = String::try_from("abcd").unwrap(); let mut std_s = std::string::String::new(); write!(std_s, "{:?}", s).unwrap(); assert_eq!("\"abcd\"", std_s); @@ -545,7 +559,7 @@ mod tests { fn display() { use core::fmt::Write; - let s: String<8> = String::from("abcd"); + let s: String<8> = String::try_from("abcd").unwrap(); let mut std_s = std::string::String::new(); write!(std_s, "{}", s).unwrap(); assert_eq!("abcd", std_s); @@ -561,10 +575,13 @@ mod tests { } #[test] - fn from() { - let s: String<4> = String::from("123"); + fn try_from() { + let s: String<4> = String::try_from("123").unwrap(); assert!(s.len() == 3); assert_eq!(s, "123"); + + let e: () = String::<2>::try_from("123").unwrap_err(); + assert_eq!(e, ()); } #[test] @@ -596,18 +613,21 @@ mod tests { #[test] #[should_panic] fn from_panic() { - let _: String<4> = String::from("12345"); + let _: String<4> = String::try_from("12345").unwrap(); } #[test] - fn from_num() { - let v: String<20> = String::from(18446744073709551615 as u64); + fn try_from_num() { + let v: String<20> = String::try_from(18446744073709551615 as u64).unwrap(); assert_eq!(v, "18446744073709551615"); + + let e: () = String::<2>::try_from(18446744073709551615 as u64).unwrap_err(); + assert_eq!(e, ()); } #[test] fn into_bytes() { - let s: String<4> = String::from("ab"); + let s: String<4> = String::try_from("ab").unwrap(); let b: Vec = s.into_bytes(); assert_eq!(b.len(), 2); assert_eq!(&['a' as u8, 'b' as u8], &b[..]); @@ -615,7 +635,7 @@ mod tests { #[test] fn as_str() { - let s: String<4> = String::from("ab"); + let s: String<4> = String::try_from("ab").unwrap(); assert_eq!(s.as_str(), "ab"); // should be moved to fail test @@ -625,7 +645,7 @@ mod tests { #[test] fn as_mut_str() { - let mut s: String<4> = String::from("ab"); + let mut s: String<4> = String::try_from("ab").unwrap(); let s = s.as_mut_str(); s.make_ascii_uppercase(); assert_eq!(s, "AB"); @@ -633,7 +653,7 @@ mod tests { #[test] fn push_str() { - let mut s: String<8> = String::from("foo"); + let mut s: String<8> = String::try_from("foo").unwrap(); assert!(s.push_str("bar").is_ok()); assert_eq!("foobar", s); assert_eq!(s, "foobar"); @@ -644,7 +664,7 @@ mod tests { #[test] fn push() { - let mut s: String<6> = String::from("abc"); + let mut s: String<6> = String::try_from("abc").unwrap(); assert!(s.push('1').is_ok()); assert!(s.push('2').is_ok()); assert!(s.push('3').is_ok()); @@ -654,13 +674,13 @@ mod tests { #[test] fn as_bytes() { - let s: String<8> = String::from("hello"); + let s: String<8> = String::try_from("hello").unwrap(); assert_eq!(&[104, 101, 108, 108, 111], s.as_bytes()); } #[test] fn truncate() { - let mut s: String<8> = String::from("hello"); + let mut s: String<8> = String::try_from("hello").unwrap(); s.truncate(6); assert_eq!(s.len(), 5); s.truncate(2); @@ -671,7 +691,7 @@ mod tests { #[test] fn pop() { - let mut s: String<8> = String::from("foo"); + let mut s: String<8> = String::try_from("foo").unwrap(); assert_eq!(s.pop(), Some('o')); assert_eq!(s.pop(), Some('o')); assert_eq!(s.pop(), Some('f')); @@ -680,7 +700,7 @@ mod tests { #[test] fn pop_uenc() { - let mut s: String<8> = String::from("é"); + let mut s: String<8> = String::try_from("é").unwrap(); assert_eq!(s.len(), 3); match s.pop() { Some(c) => { @@ -702,7 +722,7 @@ mod tests { #[test] fn clear() { - let mut s: String<8> = String::from("foo"); + let mut s: String<8> = String::try_from("foo").unwrap(); s.clear(); assert!(s.is_empty()); assert_eq!(0, s.len());