我正在尝试在 Rust 中做相当于 Ruby Enumerable.collect()
。
我有一个Option<Vec<Attachment>>
,我想从中创建Option<Vec<String>>
,在None
guid 的情况下使用String::new()
元素。
#[derive(Debug)]
pub struct Attachment {
pub guid: Option<String>,
}
fn main() {
let ov: Option<Vec<Attachment>> =
Some(vec![Attachment { guid: Some("rere34r34r34r34".to_string()) },
Attachment { guid: Some("5345345534rtyr5345".to_string()) }]);
let foo: Option<Vec<String>> = match ov {
Some(x) => {
x.iter()
.map(|&attachment| attachment.guid.unwrap_or(String::new()))
.collect()
}
None => None,
};
}
编译器中的错误很明显:
error[E0277]: the trait bound `std::option::Option<std::vec::Vec<std::string::String>>: std::iter::FromIterator<std::string::String>` is not satisfied
--> src/main.rs:15:18
|
15 | .collect()
| ^^^^^^^ the trait `std::iter::FromIterator<std::string::String>` is not implemented for `std::option::Option<std::vec::Vec<std::string::String>>`
|
= note: a collection of type `std::option::Option<std::vec::Vec<std::string::String>>` cannot be built from an iterator over elements of type `std::string::String`
如果我记得到目前为止我从文档中读到的内容,我无法为我不拥有的struct
实现特征。
如何使用iter().map(...).collect()
或其他方式执行此操作?
您应该阅读并记住Option
(和Result
)上的所有方法。这些在 Rust 中被普遍使用,知道存在的内容将极大地帮助你。
例如,您的match
语句是Option::map
。
既然你从来没有说过你不能转让String
的所有权,我就这样做。这将避免任何额外的分配:
let foo: Option<Vec<_>> =
ov.map(|i| i.into_iter().map(|a| a.guid.unwrap_or_else(String::new)).collect());
注意我们不必在Vec
中指定类型;它可以推断出来。
当然,您可以引入功能以使其更简洁:
impl Attachment {
fn into_guid(self) -> String {
self.guid.unwrap_or_else(String::new)
}
}
// ...
let foo: Option<Vec<_>> = ov.map(|i| i.into_iter().map(Attachment::into_guid).collect());
如果你不想放弃String
的所有权,你可以做同样的概念,但使用字符串切片:
impl Attachment {
fn guid(&self) -> &str {
self.guid.as_ref().map_or("", String::as_str)
}
}
// ...
let foo: Option<Vec<_>> = ov.as_ref().map(|i| i.iter().map(|a| a.guid().to_owned()).collect());
在这里,我们必须使用 Option::as_ref
来避免将guid
移出Attachment
,然后转换为带有 String::as_str
的&str
,提供默认值。我们同样不拥有ov
Option
,因此需要迭代引用,并最终分配新的String
ToOwned
。
这是一个有效的解决方案:
#[derive(Debug)]
pub struct Attachment {
pub guid: Option<String>,
}
fn main() {
let ov: Option<Vec<Attachment>> =
Some(vec![Attachment { guid: Some("rere34r34r34r34".to_string()) },
Attachment { guid: Some("5345345534rtyr5345".to_string()) }]);
let foo: Option<Vec<_>> = ov.map(|x|
x.iter().map(|a| a.guid.as_ref().unwrap_or(&String::new()).clone()).collect());
println!("{:?}", foo);
}
上述代码的问题之一是阻止guid
移出Attachment
并移入向量。我的示例调用 clone
将克隆的实例移动到向量中。
这有效,但我认为它看起来更好,包裹在Option<T>
的特质中.也许这是一个更好的...选择。。。:
trait CloneOr<T, U>
where U: Into<T>,
T: Clone
{
fn clone_or(&self, other: U) -> T;
}
impl<T, U> CloneOr<T, U> for Option<T>
where U: Into<T>,
T: Clone
{
fn clone_or(&self, other: U) -> T {
self.as_ref().unwrap_or(&other.into()).clone()
}
}
#[derive(Debug)]
pub struct Attachment {
pub guid: Option<String>,
}
fn main() {
let ov: Option<Vec<Attachment>> =
Some(vec![Attachment { guid: Some("rere34r34r34r34".to_string()) },
Attachment { guid: Some("5345345534rtyr5345".to_string()) },
Attachment { guid: None }]);
let foo: Option<Vec<_>> =
ov.map(|x| x.iter().map(|a| a.guid.clone_or("")).collect());
println!("{:?}", foo);
}
本质上,解包和克隆隐藏在附加到Option<T>
的特征实现后面。
这是它在操场上奔跑。