简体   繁体   English

如何转换 Vec<String> 到 Vec&lt;&amp;str&gt;?

[英]How do I convert a Vec<String> to Vec<&str>?

I can convert Vec<String> to Vec<&str> this way:我可以通过这种方式将Vec<String>转换为Vec<&str>

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

Are there better alternatives?有更好的选择吗?

There are quite a few ways to do it, some have disadvantages, others simply are more readable to some people.有很多方法可以做到这一点,有些有缺点,有些只是对某些人来说更具可读性。

This dereferences s (which is of type &String ) to a String "right hand side reference", which is then dereferenced through the Deref trait to a str "right hand side reference" and then turned back into a &str .这将s (类型为&String )取消引用为String “右侧引用”,然后通过Deref特性将其取消引用为str “右手侧引用”,然后再转换回&str This is something that is very commonly seen in the compiler, and I therefor consider it idiomatic.这是在编译器中非常常见的东西,因此我认为它是惯用的。

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

Here the deref function of the Deref trait is passed to the map function.在这里, deref的功能Deref性状传递给map功能。 It's pretty neat but requires use ing the trait or giving the full path.它非常简洁,但需要use trait 或提供完整路径。

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

This uses coercion syntax.这使用强制语法。

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

This takes a RangeFull slice of the String (just a slice into the entire String ) and takes a reference to it.这需要StringRangeFull切片(只是整个String一个切片)并引用它。 It's ugly in my opinion.在我看来这很丑陋。

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

This is uses coercions to convert a &String into a &str .这是使用强制转换将&String转换为&str Can also be replaced by a s: &str expression in the future.将来也可以替换为s: &str表达式。

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

The following (thanks @huon-dbaupp) uses the AsRef trait, which solely exists to map from owned types to their respective borrowed type.以下(感谢@huon-dbaupp)使用AsRef trait,它仅存在于从拥有的类型映射到它们各自的借用类型。 There's two ways to use it, and again, prettiness of either version is entirely subjective.有两种方法可以使用它,同样,任何一个版本的漂亮程度都是完全主观的。

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

and

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

My bottom line is use the v8 solution since it most explicitly expresses what you want.我的底线是使用v8解决方案,因为它最明确地表达了您想要的内容。

The other answers simply work.其他答案很简单。 I just want to point out that if you are trying to convert the Vec<String> into a Vec<&str> only to pass it to a function taking Vec<&str> as argument, consider revising the function signature as:我只想指出,如果您试图将Vec<String>转换为Vec<&str>只是为了将其传递Vec<&str>作为参数的函数,请考虑将函数签名修改为:

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

instead of:代替:

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

As pointed out by this question: Function taking both owned and non-owned string collections .正如这个问题所指出的:函数采用拥有和非拥有的字符串集合 In this way both vectors simply work without the need of conversions.通过这种方式,两个向量都可以简单地工作而无需转换。

another_items.iter().map(|item| item.deref()).collect::<Vec<&str>>()

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

All of the answers idiomatically use iterators and collecting instead of a loop, but do not explain why this is better.所有的答案都习惯性地使用迭代器和收集而不是循环,但没有解释为什么这更好。

In your loop, you first create an empty vector and then push into it.在您的循环中,您首先创建一个空向量,然后推入其中。 Rust makes no guarantees about the strategy it uses for growing factors, but I believe the current strategy is that whenever the capacity is exceeded, the vector capacity is doubled. Rust 不保证它用于增长因子的策略,但我相信当前的策略是,每当超过容量时,向量容量就会翻倍。 If the original vector had a length of 20, that would be one allocation, and 5 reallocations.如果原始向量的长度为 20,那将是一次分配和 5 次重新分配。

Iterating from a vector produces an iterator that has a "size hint".从向量进行迭代会产生一个具有“大小提示”的迭代器。 In this case, the iterator implements ExactSizeIterator so it knows exactly how many elements it will return.在这种情况下,迭代器实现了ExactSizeIterator因此它确切地知道它将返回多少个元素。 map retains this and collect takes advantage of this by allocating enough space in one go for an ExactSizeIterator . map保留了这一点,而collect通过一次性为ExactSizeIterator分配足够的空间来利用这ExactSizeIterator

You can also manually do this with:您也可以使用以下方法手动执行此操作:

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

Heap allocations and reallocations are probably the most expensive part of this entire thing by far;到目前为止,堆分配和重新分配可能是整个事情中最昂贵的部分; far more expensive than taking references or writing or pushing to a vector when no new heap allocation is involved.当不涉及新的堆分配时,比获取引用或写入或推送到向量要昂贵得多。 It wouldn't surprise me if pushing a thousand elements onto a vector allocated for that length in one go were faster than pushing 5 elements that required 2 reallocations and one allocation in the process.如果一次性将一千个元素推送到为该长度分配的向量上比推送需要 2 次重新分配和在此过程中进行一次分配的 5 个元素更快,我不会感到惊讶。

Another unsung advantage is that using the methods with collect do not store in a mutable variable which one should not use if it's unneeded.另一个不为人知的优点是,使用带有collect的方法不会存储在不需要时不应使用的可变变量中。

这个使用collect

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

Here is another option:这是另一种选择:

use std::iter::FromIterator;

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

Note that String::as_str is stable since Rust 1.7.注意String::as_str从 Rust 1.7 String::as_str就稳定了。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM