【问题标题】:How do I convert a Vec<String> to Vec<&str>?如何将 Vec<String> 转换为 Vec<&str>?
【发布时间】:2015-10-19 14:04:18
【问题描述】:

我可以通过这种方式将Vec&lt;String&gt; 转换为Vec&lt;&amp;str&gt;

let mut items = Vec::<&str>::new();
for item in &another_items {
    items.push(item);
}

有更好的选择吗?

【问题讨论】:

    标签: string vector rust type-conversion


    【解决方案1】:

    有很多方法可以做到,有些有缺点,有些对某些人来说更易读。

    这会将s(类型为&amp;String)取消引用到String“右侧引用”,然后通过Deref 特征将其取消引用到str“右侧引用”然后又变成了&amp;str。这是在编译器中很常见的东西,因此我认为它是惯用的。

    let v2: Vec<&str> = v.iter().map(|s| &**s).collect();
    

    这里将Deref trait 的deref 函数传递给map 函数。它非常简洁,但需要 useing 特征或提供完整路径。

    let v3: Vec<&str> = v.iter().map(std::ops::Deref::deref).collect();
    

    这使用强制语法。

    let v4: Vec<&str> = v.iter().map(|s| s as &str).collect();
    

    这需要StringRangeFull 切片(只是整个String 的切片)并引用它。在我看来这很丑。

    let v5: Vec<&str> = v.iter().map(|s| &s[..]).collect();
    

    这是使用强制转换将&amp;String 转换为&amp;str。将来也可以替换为s: &amp;str 表达式。

    let v6: Vec<&str> = v.iter().map(|s| { let s: &str = s; s }).collect();
    

    以下(感谢@huon-dbaupp)使用AsRef trait,它仅用于从拥有的类型映射到它们各自的借用类型。有两种使用方式,同样,任何一个版本的美观程度都完全是主观的。

    let v7: Vec<&str> = v.iter().map(|s| s.as_ref()).collect();
    

    let v8: Vec<&str> = v.iter().map(AsRef::as_ref).collect();
    

    我的底线是使用v8 解决方案,因为它最明确地表达了您的需求。

    【讨论】:

    • 显式调用deref(尤其是作为免费功能)在IMO中读起来很奇怪,我个人觉得v2v5更好。 (也可以使用as_refv.iter().map(|s| s.as_ref())...,这是这种形式的泛型转换的典型方法,例如std中的许多函数采用P: AsRef&lt;Path&gt;。)
    • 谢谢,你是完全正确的。 AsRef 在这里更合适。我更新了帖子
    • 在 v8 示例中,您还可以调用 String::as_str,这样可以更清楚地了解您的代码的意图。
    • s as &amp;str 对我来说最有意义,惯用的方式很难理解和记住 IMO。
    • 那么,没有分配就没有办法了吗?? collect 分配一个新向量。
    【解决方案2】:

    其他答案很简单。我只想指出,如果您尝试将 Vec&lt;String&gt; 转换为 Vec&lt;&amp;str&gt; 只是为了将其传递给以 Vec&lt;&amp;str&gt; 作为参数的函数,请考虑将函数签名修改为:

    fn my_func<T: AsRef<str>>(list: &[T]) { ... }
    

    代替:

    fn my_func(list: &Vec<&str>) { ... }
    

    正如这个问题所指出的:Function taking both owned and non-owned string collections。这样,两个向量都可以简单地工作而无需转换。

    【讨论】:

      【解决方案3】:
      another_items.iter().map(|item| item.deref()).collect::<Vec<&str>>()
      

      要使用deref(),您必须使用use std::ops::Deref 添加

      【讨论】:

        【解决方案4】:

        这个用collect

        let strs: Vec<&str> = another_items.iter().map(|s| s as &str).collect();
        

        【讨论】:

          【解决方案5】:

          所有答案都习惯性地使用迭代器和收集而不是循环,但没有解释为什么这样做更好。

          在您的循环中,您首先创建一个空向量,然后将其推入。 Rust 不保证它用于增长因子的策略,但我相信当前的策略是,每当超过容量时,向量容量就会增加一倍。如果原始向量的长度为 20,那将是 1 次分配和 5 次重新分配。

          从向量迭代产生一个具有“大小提示”的迭代器。在这种情况下,迭代器实现了ExactSizeIterator,因此它确切地知道它将返回多少个元素。 map 保留了这一点,collect 通过一次性为ExactSizeIterator 分配足够的空间来利用这一点。

          您也可以手动执行此操作:

          let mut items = Vec::<&str>::with_capacity(another_items.len());
          for item in &another_items {
              items.push(item);
          }
          

          到目前为止,堆分配和重新分配可能是整个事情中最昂贵的部分;比在不涉及新的堆分配时获取引用或写入或推送到向量要昂贵得多。如果一次性将一千个元素推送到分配给该长度的向量上比推送需要 2 次重新分配和一次分配的 5 个元素更快,我不会感到惊讶。

          另一个不为人知的优势是,使用 collect 的方法不会存储在不需要时不应使用的可变变量中。

          【讨论】:

          • 这似乎没有回答问题:“如何将Vec&lt;String&gt; 转换为Vec&lt;&amp;str&gt;?”
          • @Shepmaster 正确,已经在很多方面得到了回答。我只是指出他们都忽略了回答为什么他们的答案是惯用的 Rust 反对 ops 建议。正确地说他们更好,但没有解释为什么更好,对我来说似乎是一个糟糕的学习练习。
          【解决方案6】:

          这是另一种选择:

          use std::iter::FromIterator;
          
          let v = Vec::from_iter(v.iter().map(String::as_str));
          

          请注意,String::as_str 自 Rust 1.7 以来是稳定的。

          【讨论】:

            猜你喜欢
            • 2021-11-30
            • 2021-03-16
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2017-05-01
            • 1970-01-01
            相关资源
            最近更新 更多