Skip to content

alloc: add useful try_* methods to String #476

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: rust
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
163 changes: 161 additions & 2 deletions rust/alloc/string.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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);
Comment on lines +463 to +468
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps you can simply assert_eq!(s.capacity(), 10); before and after, since the capacity is known (i.e. we are guaranteeing exact capacity, rather than "at least", no?).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We indeed guarantee it. From the capacity docs of Vec:

vec![x; n], vec![a, b, c, d], and Vec::with_capacity(n), will all produce a Vec with exactly the requested capacity.

///
/// // ...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<String, TryReserveError> {
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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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`].
Expand Down Expand Up @@ -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<Box<str>, TryReserveError> {
let slice = self.vec.try_into_boxed_slice()?;
Ok(unsafe { from_boxed_utf8_unchecked(slice) })
}
}

impl FromUtf8Error {
Expand Down