我可以这样将Vec<String>转换为Vec<&str>:
Vec<String>
Vec<&str>
let another_items = vec!["foo".to_string(), "bar".to_string()]; let mut items = Vec::<&str>::new(); for item in &another_items { items.push(item); }
有更好的选择吗?
7kjnsjlb1#
有很多方法可以做到这一点,有些有缺点,其他人只是更可读的一些人。这将s(类型为&String)解引用到String“右侧引用”,然后通过Deref trait解引用到str“右侧引用”,然后返回到&str。这在编译器中很常见,因此我认为它是惯用的。
s
&String
String
Deref
str
&str
let v2: Vec<&str> = v.iter().map(|s| &**s).collect();
这里,Deref trait的deref函数被传递给map函数。这很简洁,但需要use特征或给出完整的路径。
deref
map
use
let v3: Vec<&str> = v.iter().map(std::ops::Deref::deref).collect();
这使用强制语法。
let v4: Vec<&str> = v.iter().map(|s| s as &str).collect();
这将获取String的一个RangeFull切片(只是整个String的一个切片)并引用它。在我看来很丑。
RangeFull
let v5: Vec<&str> = v.iter().map(|s| &s[..]).collect();
这是使用强制性将&String转换为&str。将来也可以用s: &str表达式替换。
s: &str
let v6: Vec<&str> = v.iter().map(|s| { let s: &str = s; s }).collect();
下面的代码(感谢@huon-dbaupp)使用了AsRef trait,它的存在只是为了从拥有的类型Map到它们各自的借用类型。有两种方法来使用它,再次,任何一个版本的漂亮是完全主观的。
AsRef
let v7: Vec<&str> = v.iter().map(|s| s.as_ref()).collect();
和
let v8: Vec<&str> = v.iter().map(AsRef::as_ref).collect();
我的底线是使用v8解决方案,因为它最明确地表达了您想要的东西。
v8
enxuqcxy2#
其他答案只是工作。我只想指出,如果你试图将Vec<String>转换为Vec<&str>,只是为了传递给一个以Vec<&str>为参数的函数,请考虑将函数签名修改为:
fn my_func<T: AsRef<str>>(list: &[T]) { ... }
而不是:
fn my_func(list: &Vec<&str>) { ... }
正如这个问题所指出的:Function taking both owned and non-owned string collections。通过这种方式,两个向量都可以简单地工作,而不需要转换。
4smxwvx53#
所有的答案都习惯性地使用迭代器和收集而不是循环,但没有解释为什么这样更好。在循环中,首先创建一个空向量,然后推入其中。Rust没有保证它对增长因子所使用的策略,但我相信当前的策略是,每当超过容量时,向量容量就会增加一倍。如果原始向量的长度为20,则这将是一个分配和5个重新分配。从一个向量迭代产生一个具有“大小提示”的迭代器。在这种情况下,迭代器实现了ExactSizeIterator,因此它确切地知道它将返回多少个元素。map保留了这一点,collect通过一次性为ExactSizeIterator分配足够的空间来利用这一点。您也可以使用以下命令手动执行此操作:
ExactSizeIterator
collect
let mut items = Vec::<&str>::with_capacity(another_items.len()); for item in &another_items { items.push(item); }
堆分配和重新分配可能是到目前为止整个过程中最昂贵的部分;比在不涉及新堆分配的情况下获取引用或写入或推送到向量要昂贵得多。如果一次性将1000个元素推送到为该长度分配的向量上比推送5个元素更快,这并不奇怪,因为在这个过程中需要2次重新分配和一次分配。另一个不为人知的优点是,使用collect的方法不会存储在一个可变变量中,如果不需要,就不应该使用这个变量。
qojgxg4l4#
another_items.iter().map(|item| item.deref()).collect::<Vec<&str>>()
要使用deref(),必须使用use std::ops::Deref添加
deref()
use std::ops::Deref
uqzxnwby5#
这个使用collect:
let strs: Vec<&str> = another_items.iter().map(|s| s as &str).collect();
s8vozzvw6#
这里是另一个选项:
use std::iter::FromIterator; let v = Vec::from_iter(v.iter().map(String::as_str));
请注意,String::as_str从Rust 1开始就稳定了。7.
String::as_str
6条答案
按热度按时间7kjnsjlb1#
有很多方法可以做到这一点,有些有缺点,其他人只是更可读的一些人。
这将
s
(类型为&String
)解引用到String
“右侧引用”,然后通过Deref
trait解引用到str
“右侧引用”,然后返回到&str
。这在编译器中很常见,因此我认为它是惯用的。这里,
Deref
trait的deref
函数被传递给map
函数。这很简洁,但需要use
特征或给出完整的路径。这使用强制语法。
这将获取
String
的一个RangeFull
切片(只是整个String
的一个切片)并引用它。在我看来很丑。这是使用强制性将
&String
转换为&str
。将来也可以用s: &str
表达式替换。下面的代码(感谢@huon-dbaupp)使用了
AsRef
trait,它的存在只是为了从拥有的类型Map到它们各自的借用类型。有两种方法来使用它,再次,任何一个版本的漂亮是完全主观的。和
我的底线是使用
v8
解决方案,因为它最明确地表达了您想要的东西。enxuqcxy2#
其他答案只是工作。我只想指出,如果你试图将
Vec<String>
转换为Vec<&str>
,只是为了传递给一个以Vec<&str>
为参数的函数,请考虑将函数签名修改为:而不是:
正如这个问题所指出的:Function taking both owned and non-owned string collections。通过这种方式,两个向量都可以简单地工作,而不需要转换。
4smxwvx53#
所有的答案都习惯性地使用迭代器和收集而不是循环,但没有解释为什么这样更好。
在循环中,首先创建一个空向量,然后推入其中。Rust没有保证它对增长因子所使用的策略,但我相信当前的策略是,每当超过容量时,向量容量就会增加一倍。如果原始向量的长度为20,则这将是一个分配和5个重新分配。
从一个向量迭代产生一个具有“大小提示”的迭代器。在这种情况下,迭代器实现了
ExactSizeIterator
,因此它确切地知道它将返回多少个元素。map
保留了这一点,collect
通过一次性为ExactSizeIterator
分配足够的空间来利用这一点。您也可以使用以下命令手动执行此操作:
堆分配和重新分配可能是到目前为止整个过程中最昂贵的部分;比在不涉及新堆分配的情况下获取引用或写入或推送到向量要昂贵得多。如果一次性将1000个元素推送到为该长度分配的向量上比推送5个元素更快,这并不奇怪,因为在这个过程中需要2次重新分配和一次分配。
另一个不为人知的优点是,使用
collect
的方法不会存储在一个可变变量中,如果不需要,就不应该使用这个变量。qojgxg4l4#
要使用
deref()
,必须使用use std::ops::Deref
添加uqzxnwby5#
这个使用
collect
:s8vozzvw6#
这里是另一个选项:
请注意,
String::as_str
从Rust 1开始就稳定了。7.