简体   繁体   中英

Generic operation on slice of Cow<str>

I'm trying to implement the following code, which removes the prefix from a slice of Cow<str> 's.

fn remove_prefix(v: &mut [Cow<str>], prefix: &str) {
    for t in v.iter_mut() {
        match *t {
            Borrowed(&s) => s = s.trim_left_matches(prefix),
            Owned(s) => s = s.trim_left_matches(prefix).to_string(),
        }
    }
}

I have two questions:

  1. I can't get this to compile - I've tried loads of combinations of & 's and * 's but to no avail.

  2. Is there a better way to apply functions to a Cow<str> without having to match it to Borrowed and Owned every time. I mean it seems like I should just be able to do something like *t = t.trim_left_matches(prefix) and if t is a Borrowed(str) it leaves it as a str (since trim_left_matches allows that), and if it is an Owned(String) it leaves it as a String . Similarly for replace() it would realise it has to convert both to a String (since you can't use replace() on a str ). Is something like that possible?

Question #1 strongly implies how you think pattern matching and/or pointers work in Rust doesn't quite line up with how they actually work. The following code compiles:

fn remove_prefix(v: &mut [Cow<str>], prefix: &str) {
    use std::borrow::Cow::*;
    for t in v.iter_mut() {
        match *t {
            Borrowed(ref mut s) => *s = s.trim_left_matches(prefix),
            Owned(ref mut s) => *s = s.trim_left_matches(prefix).to_string(),
        }
    }
}

If your case, Borrowed(&s) is matched against Borrowed(&str) , meaning that s is of type str . This is impossible: you absolutely cannot have a variable of a dynamically sized type. It's also counter-productive. Given that you want to modify s , binding to it by value won't help at all.

What you want is to modify the thing contained in the Borrowed variant. This means you want a mutable pointer to that storage location. Hence, Borrowed(ref mut s) : this is not destructuring the value inside the Borrowed at all. Rather, it binds directly to the &str , meaning that s is of type &mut &str ; a mutable pointer to a (pointer to a str ). In other words: a mutable pointer to a string slice.

At that point, mutating the contents of the Borrowed is done by re-assigning the value through the mutable pointer: *s = ... .

Finally, the exact same reasoning applies to the Owned case: you were trying to bind by-value, then mutate it, which cannot possibly do what you want. Instead, bind by mutable pointer to the storage location, then re-assign it.

As for question #2... not really. That would imply some kind of overloading, which Rust doesn't do (by deliberate choice). If you are doing this a lot , you could write an extension trait that adds methods of interest to Cow .

You can definitely do it.

fn remove_prefix(v: &mut [Cow<str>], prefix: &str) {
    for t in v.iter_mut() {
        match *t {
            Cow::Borrowed(ref mut s) => *s = s.trim_left_matches(prefix),
            Cow::Owned(ref mut s) => *s = s.trim_left_matches(prefix).to_string(),
        }
    }
}

ref mut s means “take a mutable reference to the value and call it s ” in a pattern. Thus you have s of type &mut &str or &mut String . You must then use *s = in order to change what that mutable reference is pointing to (thus, change the string inside the Cow ).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM