-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Description
The current API for manipulating String
objects is largely relegated to str
, and many operations treat the source string as immutable and return a new &str
reference with the result of the operation (unless non-contiguous results are not guaranteed, e.g. .replace()
, etc.)
Oftentimes destructive modifications to a String
are needed "while it is being created" and developers are forced to either write their own logic to duplicate str
functionality to update String
values or else use .to_owned()
on the result of str
functions.
e.g. a function runs a command and returns its output as a string, with trailing whitespace removed:
....
let mut path = String::from_utf8(output.stdout)?;
let new_len = path.trim_end().len();
path.truncate(new_len);
Ok(path)
vs
....
let path = String::from_utf8(output.stdout)?;
let path_str = path.trim_end();
Ok(path_str.to_owned())
I believe we're close to being able to support a safe mutating api that provides a &str
and uses an &str
result to redefine the string (mainly to pop or dequeue elements from the underlying storage), something along the lines of
fn String::mutate(&mut self, F) -> ()
where F: Fn(&'restricted str) -> &'restricted str`
which passes in an (exclusive) reference to the current value of the string and expects to receive a subset: &'restricted str
as a result, where self.as_bytes() == &[.., subset.as_bytes(), ..]
holds true (i.e. subset.as_bytes().as_ptr() >= self.as_bytes().as_ptr() && subset.as_bytes().as_ptr() + subset.as_bytes().len() < self.as_bytes().as_ptr() + self.as_bytes().len()
)
This makes the most sense it if it is possible to make compile-time guarantees ensuring the above predicate. I don't think #2000 via rust-lang/rust#44580 currently makes this possible, but I feel as if there is overlap between const generics and the ability to do so.
Using it would look something like this:
let mut start = " hello, world ".to_owned();
// Now trim both ends of the string without reallocating
start.mutate(|s| s.trim());
assert_eq!(&start, "hello, world");
while forbidding the following:
let other_string = "not my string";
let mut start = " hello, world ".to_owned();
// Try to provide a reference to an str that is not a subset of the string itself
start.mutate(|_| &other_string); // should not compile