pre-RFC: `str::replace_with` function to replace text with closure

Rust has a lot of idiomatic approaches to common things, like Iterator::enumerate or str::char_indices to iterate over both the index and the element(char in str).
Similarly replace_with(not limited to just strs) could be used to replace a pattern with the result of a closure, like so:

let str = "{}, {}!";
let new_str = str.replace_with("{}", |i| if i == 0 {
    i += 1;
    "Hello"
} else {
    "World"
);

So you don't need to write verbose for loops.
In the str, it could be declared as:

pub fn replace_with<P, F>(&self, pattern: P, closure: F) -> &str
where
    P: Pattern,
    F: FnMut(usize /* position of pattern */, P /* actual pattern (if there are multiple and you want to check which one is) */) -> &str {}

Where if an iterator implementation is included, the pattern in the closure would not be needed, and a slice of Iterator::Items could be used instead of just one, so that you can provide different code for different patterns.

This looks better suited to internals.rust-lang.org. This forum is for discussing how to use current Rust, not how to change Rust.

2 Likes

Still, i want to see whether or not this would be a good addition to the users.

It looks like this would essentially duplicate a less powerful subset of Regex::replace(), so given that the latter exists, I don't see why this should be in std.

Anyway, if you want this API, it's pretty straightforward to implement by yourself:

fn replace_with<'a, P, F, S>(&'a self, pattern: P, mut replacer: F) -> Cow<'a, str>
where
    P: Pattern<'a>,
    F: FnMut(usize, usize, &'a str) -> S,
    S: AsRef<str>,
{
    let mut result = String::new();
    let mut lastpos = 0;
    
    for (idx, (pos, substr)) in self.match_indices(pattern).enumerate() {
        result.push_str(&self[lastpos..pos]);
        lastpos = pos + substr.len();
        let replacement = replacer(idx, pos, substr);
        result.push_str(replacement.as_ref());
    }
    
    if lastpos == 0 {
        Cow::Borrowed(self)
    } else {
        result.push_str(&self[lastpos..]);
        Cow::Owned(result)
    }
}

Thank you, now i know that i won't need to open an rfc just for that.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.