Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
721 views
in Technique[技术] by (71.8m points)

rust - How do I write a function that takes both owned and non-owned string collections?

I'm having trouble writing a function that takes a collection of strings as parameter. My function looks like this:

type StrList<'a> = Vec<&'a str>;

fn my_func(list: &StrList) {
    for s in list {
        println!("{}", s);
    }
}

All goes well if I pass a Vec<&'a str> to the function, as expected. However, if I pass a Vec<String> the compiler complains:

error[E0308]: mismatched types
  --> src/main.rs:13:13
   |
13 |     my_func(&v2);
   |             ^^^ expected &str, found struct `std::string::String`
   |
   = note: expected type `&std::vec::Vec<&str>`
   = note:    found type `&std::vec::Vec<std::string::String>`

This is the main used:

fn main() {
    let v1 = vec!["a", "b"];
    let v2 = vec!["a".to_owned(), "b".to_owned()];
    my_func(&v1);
    my_func(&v2);
}

My function is not able to take vectors of owned strings. Conversely, if I change the StrList type into:

type StrList = Vec<String>;

The first call fails, and the second works.

A possible solution is to produce a Vec<&'a str> from v2 in this way:

let v2_1 : Vec<_> = v2.iter().map(|s| s.as_ref()).collect();

But it seems very odd to me. my_func should not care about the ownership of the strings.

What kind of signature should I use for my_func to support both vectors of owned strings and string references?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Although String and &str are very closely related, they are not identical. Here's what your vectors look like in memory:

v1 ---> [ { 0x7890, // pointer to "a" + 7 unused bytes
            1 }     // length of "a"
          { 0x7898, // pointer to "b" + 7 unused bytes
            1 } ]   // length

v2 ---> [ { 0x1230 // pointer to "a" + 7 unused bytes (a different copy)
            8      // capacity
            1 }    // length
          { 0x1238 // pointer ...
            8      // capacity
            1 } ]  // length

Here each line is the same amount of memory (four or eight bytes depending on pointer size). You can't take the memory of one of these and treat it like the other. The memory layout doesn't match up. The items are of different sized and have different layout. For example, if v1 stores its items starting at address X and v2 stores its items starting at address Y, then v1[1] is at address X + 8 but v2[1] is at address Y + 12.

What you can do is write a generic function like this:

fn my_func<T: AsRef<str>>(list: &[T]) {
    for s in list {
        println!("{}", s.as_ref());
    }
}

Then the compiler can generate appropriate code for both &[String] and &[&str] as well as other types if they implement AsRef<str>.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...