rust-lang / rust-lang/rfcs

Make alloc-free string manipulation more ergonomic with String::mutate(&mut self, ...)

Open
#2,864 16 comments 4 reactions 0 assignees View on GitHub

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

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.