为什么我无法从对带有 String 的结构体的引用的迭代器中收集 Vec<&str> ?

Har*_*751 5 rust

我目前正在学习 Rust,我想从拥有字符串的项目向量中收集字符串 (&strs) 的引用。

struct Channel {
    cid: usize,
    name: String,
}

fn main() {
    let channels: Vec<Channel> = vec![Channel {
        cid: 1,
        name: "Hello".to_string(),
    }];
    let names: Vec<&str> = channels.iter().map(|x| &x.name).collect();
}
Run Code Online (Sandbox Code Playgroud)

此代码无法编译,出现以下错误:

[E0277]: a value of type `Vec<&str>` cannot be built from an iterator over elements of type `&String`
    --> src/main.rs:11:61
     |
11   |     let names: Vec<&str> = channels.iter().map(|x| &x.name).collect();
     |                                                             ^^^^^^^ value of type `Vec<&str>` cannot be built from `std::iter::Iterator<Item=&String>`
     |
     = help: the trait `FromIterator<&String>` is not implemented for `Vec<&str>`
     = help: the trait `FromIterator<&str>` is implemented for `Vec<&str>`
     = help: for that trait implementation, expected `str`, found `String`
note: the method call chain might not have had the expected associated types
    --> src/main.rs:11:44
     |
7    |       let channels: Vec<Channel> = vec![Channel {
     |  __________________________________-
8    | |         cid: 1,
9    | |         name: "Hello".to_string(),
10   | |     }];
     | |______- this expression has type `Vec<Channel>`
11   |       let names: Vec<&str> = channels.iter().map(|x| &x.name).collect();
     |                                       ------ ^^^^^^^^^^^^^^^^ `Iterator::Item` changed to `&String` here
     |                                       |
     |                                       `Iterator::Item` is `&Channel` here
note: required by a bound in `collect`
    --> /playground/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/iter/traits/iterator.rs:2050:19
     |
2050 |     fn collect<B: FromIterator<Self::Item>>(self) -> B
     |                   ^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Iterator::collect`
Run Code Online (Sandbox Code Playgroud)

可以通过调用 as_str() 来提取字符串切片来使其工作:

let names: Vec<&str> = channels.iter().map(|x| x.name.as_str()).collect();

但是,我想了解为什么需要这样做。根据我的理解,由于 deref 强制, a&String可以转换为 a &str。为什么我必须明确使用as_str()?此外,就性能而言,获取as_str()与引用某个项目相同吗?

Cha*_*man 12

Deref 强制通常按您的预期工作,但它取决于类型推断的棘手细节。特别是,在这种情况下,编译器得出闭包返回的结论&String,当它意识到我们需要&str它时已经太晚了(在 之后collect())并且它不会及时返回来修复它。

除了以下之外,还有多种方式提示编译器as_str()

// Notice the `as _`.
let names: Vec<&str> = channels.iter().map(|x| &x.name as _).collect();
Run Code Online (Sandbox Code Playgroud)
let names: Vec<&str> = channels.iter().map(|x| -> &str { &x.name }).collect();
Run Code Online (Sandbox Code Playgroud)
let names: Vec<&str> = channels.iter().map(|x| &*x.name).collect();
Run Code Online (Sandbox Code Playgroud)

此外,就性能而言,获取as_str()与引用某个项目相同吗?

是的。它只是显式地执行编译器通常隐式执行的操作,但无论哪种方式都需要完成工作。