Make alloc-free string manipulation more ergonomic with String::mutate(&mut self, ...)
Nobody has claimed this yet.
- Dominant language
- Markdown
- Stars
- 6.6k
- Forks
- 1.7k
- Avg merge
- 16h 14m
- Merged PRs (30d)
- 1
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 https://github.com/rust-lang/rust/issues/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
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
No repository files or tests are named. Start by reading the proposed String::mutate API and the referenced issues #2000 and #44580, then determine whether Rust can enforce the required subset-reference guarantee; the work is done only when the API design and its safety constraints are settled.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- compilers
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Needs clarification
- Newbie friendliness
- 20/100