在Rust中如何将Vec<String>转换为&str切片?

56

根据Steve Klabnik在Rust 1.0文档之前的写作中关于String&str之间区别的文章, 在Rust中,除非你真正需要拥有一个String,否则应该使用&str。同样地,建议使用对切片(&[])的引用而不是Vec,除非你真正需要拥有Vec

我有一个Vec<String>,我想编写一个函数来使用这个字符串序列,它不需要对VecString实例进行所有权控制,那么这个函数应该采用&[&str]吗?如果是这样,将Vec<String>引用到&[&str]的最佳方式是什么?或者,这种转换是否过度繁琐?

2个回答

70

您可以使用AsRef特性创建一个接受&[String]&[&str]两种类型的函数:

fn test<T: AsRef<str>>(inp: &[T]) {
    for x in inp { print!("{} ", x.as_ref()) }
    println!("");
}

fn main() {
    let vref = vec!["Hello", "world!"];
    let vown = vec!["May the Force".to_owned(), "be with you.".to_owned()];
    test(&vref);
    test(&vown);
}

6
我认为这就是原问题提问者在寻找的答案,因为它允许使用切片而不必进行不必要的内存分配。当接受一个AsRef<Path>类型的切片时,这种方法会更加有用——你希望函数能够接受所有&[&str]&[String]&[Path]&[PathBuf]的切片,而不需要分配新的内存。 - user4815162342
有一件令人沮丧的事情是,如果您尝试获取 Option<&[T]>,则不能只传递 None 而不指定具体类型。 - klefevre

20

没有内存分配或每个元素调用是不可能的1

String&str 不仅仅是以不同的方式查看位;String&str 有不同的内存布局,因此从一个到另一个需要创建一个新对象。对于 Vec&[] 也是一样。

因此,虽然你可以从 Vec<T> 转换为 &[T],从而从 Vec<String> 转换为 &[String],但你不能直接从 Vec<String> 转换为 &[&str]。你的选择是:

  • 接受 &[String]
  • 分配一个新的引用第一个 VecVec<&str>,并将其转换为 &[&str]

以下是分配的示例:

fn usage(_: &[&str]) {}

fn main() {
    let owned = vec![String::new()];

    let half_owned: Vec<_> = owned.iter().map(String::as_str).collect();

    usage(&half_owned);
}

1 使用泛型和如@aSpex所示的AsRef<str>绑定,您可以获得略微冗长但具有所需灵活性的函数声明,但您必须在所有元素中调用.as_ref()


谢谢,Matthieu。对于我现在的情况,我想我会选择 &[String],因为我想分配一个新的 Vec<&str> 会产生额外的工作。 - Don Rowe
2
@DonRowe:这将产生额外的分配(O(1)但潜在昂贵)+转换(O(n))。 - Matthieu M.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接