Produce Option<Vec> <String>out of a Option<Vec<Custom>> in Rust



我正在尝试在 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>的特征实现后面。

这是它在操场上奔跑。

最新更新