From 10b3c97c847cd59a2448398a494095742deeef44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Jab=C5=82o=C5=84ski?= Date: Sat, 31 Jul 2021 20:50:19 +0000 Subject: [PATCH] alloc: add useful `try_*` methods to `String` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit adds the following methods to `String` structure: - `try_with_capacity` - `try_push_str` - `try_shrink_to_fit` - `try_push` - `try_into_boxed_str` Those are panic-free equivalents of already existing methods. They may be useful for `String` creation inside kernel code. Signed-off-by: Mateusz Jabłoński --- rust/alloc/string.rs | 163 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 161 insertions(+), 2 deletions(-) diff --git a/rust/alloc/string.rs b/rust/alloc/string.rs index 55293c3041e7cf..03add0817bedf6 100644 --- a/rust/alloc/string.rs +++ b/rust/alloc/string.rs @@ -68,9 +68,9 @@ use core::str::pattern::Pattern; use crate::borrow::{Cow, ToOwned}; use crate::boxed::Box; use crate::collections::TryReserveError; -use crate::str::{self, Chars, Utf8Error}; +use crate::str::{self, from_boxed_utf8_unchecked, Chars, Utf8Error}; #[cfg(not(no_global_oom_handling))] -use crate::str::{from_boxed_utf8_unchecked, FromStr}; +use crate::str::FromStr; use crate::vec::Vec; /// A UTF-8–encoded, growable string. @@ -428,6 +428,59 @@ impl String { String { vec: Vec::with_capacity(capacity) } } + /// Tries to create a new empty `String` with a particular capacity. + /// + /// `String`s have an internal buffer to hold their data. The capacity is + /// the length of that buffer, and can be queried with the [`capacity`] + /// method. This method creates an empty `String`, but one with an initial + /// buffer that can hold `capacity` bytes. This is useful when you may be + /// appending a bunch of data to the `String`, reducing the number of + /// reallocations it needs to do. + /// + /// [`capacity`]: String::capacity + /// + /// If the given capacity is `0`, no allocation will occur, and this method + /// is identical to the [`new`] method. + /// + /// [`new`]: String::new + /// + /// # Errors + /// + /// If the capacity overflows, or the allocator reports a failure, then an error + /// is returned. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let mut s = String::try_with_capacity(10).unwrap(); + /// + /// // The String contains no chars, even though it has capacity for more + /// assert_eq!(s.len(), 0); + /// + /// // These are all done without reallocating... + /// let cap = s.capacity(); + /// for _ in 0..10 { + /// s.push('a'); + /// } + /// + /// assert_eq!(s.capacity(), cap); + /// + /// // ...but this may make the string reallocate + /// s.push('a'); + /// + /// let result = String::try_with_capacity(usize::MAX); + /// assert!(result.is_err()); + /// ``` + #[inline] + #[doc(alias = "alloc")] + #[doc(alias = "malloc")] + #[stable(feature = "kernel", since = "1.0.0")] + pub fn try_with_capacity(capacity: usize) -> Result { + Ok(String { vec: Vec::try_with_capacity(capacity)? }) + } + // HACK(japaric): with cfg(test) the inherent `[T]::to_vec` method, which is // required for this method definition, is not available. Since we don't // require this method for testing purposes, I'll just stub it @@ -845,6 +898,30 @@ impl String { self.vec.extend_from_slice(string.as_bytes()) } + /// Tries to append a given string slice onto the end of this `String`. + /// + /// # Errors + /// + /// If the capacity overflows, or the allocator reports a failure, then an error + /// is returned. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let mut s = String::from("foo"); + /// + /// s.try_push_str("bar").unwrap(); + /// + /// assert_eq!("foobar", s); + /// ``` + #[inline] + #[stable(feature = "kernel", since = "1.0.0")] + pub fn try_push_str(&mut self, string: &str) -> Result<(), TryReserveError> { + self.vec.try_extend_from_slice(string.as_bytes()) + } + /// Copies elements from `src` range to the end of the string. /// /// ## Panics @@ -1092,6 +1169,32 @@ impl String { self.vec.shrink_to_fit() } + /// Tries to shrink the capacity of this `String` to match its length. + /// + /// # Errors + /// + /// If the capacity overflows, or the allocator reports a failure, then an error + /// is returned. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let mut s = String::from("foo"); + /// + /// s.reserve(100); + /// assert!(s.capacity() >= 100); + /// + /// s.try_shrink_to_fit().unwrap(); + /// assert_eq!(3, s.capacity()); + /// ``` + #[inline] + #[stable(feature = "kernel", since = "1.0.0")] + pub fn try_shrink_to_fit(&mut self) -> Result<(), TryReserveError> { + self.vec.try_shrink_to_fit() + } + /// Shrinks the capacity of this `String` with a lower bound. /// /// The capacity will remain at least as large as both the length @@ -1145,6 +1248,35 @@ impl String { } } + /// Tries to append the given [`char`] to the end of this `String`. + /// + /// # Errors + /// + /// If the capacity overflows, or the allocator reports a failure, then an error + /// is returned. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let mut s = String::from("abc"); + /// + /// s.try_push('1').unwrap(); + /// s.try_push('2').unwrap(); + /// s.try_push('3').unwrap(); + /// + /// assert_eq!("abc123", s); + /// ``` + #[inline] + #[stable(feature = "kernel", since = "1.0.0")] + pub fn try_push(&mut self, ch: char) -> Result<(), TryReserveError> { + match ch.len_utf8() { + 1 => self.vec.try_push(ch as u8), + _ => self.vec.try_extend_from_slice(ch.encode_utf8(&mut [0; 4]).as_bytes()), + } + } + /// Returns a byte slice of this `String`'s contents. /// /// The inverse of this method is [`from_utf8`]. @@ -1746,6 +1878,33 @@ impl String { let slice = self.vec.into_boxed_slice(); unsafe { from_boxed_utf8_unchecked(slice) } } + + /// Tries to convert this `String` into a [`Box`]`<`[`str`]`>`. + /// + /// This will drop any excess capacity. + /// + /// [`str`]: prim@str + /// + /// # Errors + /// + /// If the capacity overflows, or the allocator reports a failure, then an error + /// is returned. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let s = String::from("hello"); + /// + /// let b = s.try_into_boxed_str().unwrap(); + /// ``` + #[stable(feature = "kernel", since = "1.0.0")] + #[inline] + pub fn try_into_boxed_str(self) -> Result, TryReserveError> { + let slice = self.vec.try_into_boxed_slice()?; + Ok(unsafe { from_boxed_utf8_unchecked(slice) }) + } } impl FromUtf8Error {