`str::actually_strip_prefix`

We'd like to add:

pub fn actually_strip_prefix<'a, P: Pattern<'a>>(&mut &'a self, prefix: P) -> bool {
  if let Some(v) = self.strip_prefix(prefix) {
    *self = v;
    true
  } else {
    false
  }
}

to str, as well as actually_strip_suffix. Thoughts?

I don’t know.. &mut &'a str seems weird; I’ve only seen something like this in places where a trait requires this kind of pattern, e.g. the signature of read in impl Read for &[u8]. Also, you aren’t serious about the name including “actually”, are you?

&mut &str doesn't bother me particularly, though the syntax for the self argument would need to change to self: &mut &'a Self (or it could take &mut self and be implemented on &'a str).

My one concern with this besides finding a good name would be the following trap that new programmers might fall into while trying to figure out how to use this on a String:

fn foo(mut s: String) {
    s.as_str().actually_strip_prefix("bar"); // does nothing
}

(It's really easy to create, modify, and drop a temporary &str without realizing it.)

5 Likes

There's two ways to design this API - in-place or not. Rust chose to make it not in-place and there is no good reason to add the second one as well, to everyone's confusion.

2 Likes

You could name it something like strip_prefix_in_place but...

s.strip_prefix_in_place(prefix);

vs

s = s.strip_prefix(prefix);

The latter is shorter to type, so I don't see how the former is actually beneficial.

1 Like

Well, that’s not the correct workaround. More like

s = s.strip_prefix(prefix).unwrap_or(s);

and that doesn’t “remember” if some prefix was actually found and stripped, either.

There’s a similar thought behind lots of iterators, e.g. slice::Iter, not being Copy, even though they could be. It’s too easy to accidentally mutate a copy and expect the original value to have changed. On the other hand, &str does already implement Copy, so to avoid the same problem it’s better to avoid having &mut &str parameters.

1 Like

Perhaps it might be useful to have something like

split_prefix<'a>(self: &'a str, prefix: impl Pattern<'a>)
    -> (Option<&'a str>, &'a str)

(basically like split_once but divides the string into a (match, rest) pair). Would be a good building block for eg. writing simple one-off parsers as well as making OP's use case fairly ergonomic.

Might also have been better for strip_prefix return a (bool, &str) rather than Option but that ship has obviously sailed.

2 Likes

It's not really in-place tho.

It uses nested references.

... Hey maybe the lack of a good name is a good reason to avoid doing it.

Confirming this is useful for writing parsers. I recently wrote code that definitely could've used this API, as I functionally did the same thing myself.