我正在尝试实现一个函数,以返回包含(Vec<String>
)模式的所有字符串的向量,并返回到另一个Vec<String>
。
这就是我的尝试:
fn select_lines(pattern: &String, lines: &Vec<String>) -> Vec<String> {
let mut selected_lines: Vec<String> = Vec::new();
for line in *lines {
if line.contains(pattern) {
selected_lines.push(line);
}
}
selected_lines
}
使用for循环(at * lines)导致行错误。我是Rust的新手(昨天开始学习Rust!),现在几乎无法解决这个错误。
我可以移除*
并且该错误消失但有关类型不匹配的错误开始达到高潮。我想保持功能的签名完好无损。有办法吗?
答案 0 :(得分:5)
问题在于您是否试图移动String
参数的lines
实例 的所有权(这是一个输入参数)...将所有权转移到返回值(输出)。
有几种选择。
Clone
最简单的方法就是克隆这些线:
selected_lines.push(line.clone());
既然你已克隆了这些界限......那就没有所有权问题了。您正在返回的内容是向量中的String
个新实例。它们只是你传入的副本。
另一个选择(避免额外的分配)是让编译器知道你不会返回任何悬挂的引用:
// introduce a lifetime to let the compiler know what you're
// trying to do. This lifetime basically says "the Strings I'm returning
// in the vector live for at least as long as the Strings coming in
fn select_lines<'a>(pattern: &String, lines: &'a Vec<String>) -> Vec<&'a String> {
let mut selected_lines: Vec<&String> = Vec::new();
for line in lines {
if line.contains(pattern) {
selected_lines.push(line);
}
}
selected_lines
}
这就是你如何解决眼前问题的方法。
如果我写这个,我会略微改变它。这是另一个旋转:
fn select_lines<I>(pattern: I, lines: &[I]) -> Vec<&str>
where
I: AsRef<str>,
{
let mut selected_lines: Vec<&str> = Vec::new();
for line in lines {
if line.as_ref().contains(pattern.as_ref()) {
selected_lines.push(line.as_ref());
}
}
selected_lines
}
您可以将此版本与String
或&str
s,向量或切片一起使用。
let lines = vec!["Hello", "Stack", "overflow"];
let selected = select_lines("over", &lines);
// prints "overflow"
for l in selected {
println!("Line: {}", l);
}
let lines2 = [String::from("Hello"), String::from("Stack"), "overflow".into()];
let selected2 = select_lines(String::from("He"), &lines2);
// prints "Hello"
for l in selected2 {
println!("Line again: {}", l);
}
答案 1 :(得分:1)
其他答案是正确的,但惯用的解决方案不会涉及重新发明轮子:
fn main() {
let lines = vec!["Hello", "Stack", "overflow"];
// Vec<String>
let selected: Vec<_> = lines
.iter()
.filter(|l| l.contains("over"))
.cloned()
.collect();
println!("{:?}", selected);
// Vec<&String>
let selected: Vec<_> = lines
.iter()
.filter(|l| l.contains("over"))
.collect();
println!("{:?}", selected);
}
另见:
答案 2 :(得分:0)
简短版本:取消取消引用并改为push(line.clone())
。
请使用以下代码:
fn main() {
let mut foo = vec![1, 2, 3, 4, 5];
for num in foo {
println!("{}", num);
}
foo.push(6);
}
运行此代码时,会引发以下错误:
error[E0382]: use of moved value: `foo`
--> src/main.rs:8:5
|
4 | for num in foo {
| --- value moved here
...
8 | foo.push(6);
| ^^^ value used here after move
|
= note: move occurs because `foo` has type `std::vec::Vec<i32>`, which does not implement the `Copy` trait
此错误上升是因为Rust for
循环获取有问题的迭代器的所有权,特别是通过IntoIterator
特征。上述代码中的for循环可以等效地写为for num in foo.into_iter()
。
请注意signature of into_iter()
。它需要self
而不是&self
;换句话说,值的所有权被移动到函数中,这将创建一个在for
循环中使用的迭代器,并且在循环结束时删除生成的迭代器。因此,为什么上面的代码失败了:我们试图使用一个被移交的变量&#34;别的东西。在其他语言中,使用的典型术语是用于循环的值消耗。
承认这种行为会让我们找到问题的根源,即&#34;移动&#34;在cannot move out of borrowed content
。当您有一个引用时,例如lines
(对Vec
的引用),您只有 - 借用。您没有该对象的所有权,因此您无法将该对象的所有权提供给其他内容,这可能会导致Rust旨在阻止的内存错误。解除引用lines
有效地说&#34;我想将原始载体提供给此循环&#34;,您无法做到,因为原始载体属于其他人
松散地说 - 我可能在这方面做错了,如果我是的话请有人纠正我 - 但在大多数情况下,在Rust中取消引用只对修改作业左侧的对象有用,因为基本上任何在表达式右侧使用取消引用都会尝试移动该项。例如:
fn main() {
let mut foo = vec![1, 2, 3, 4, 5];
println!("{:?}", foo);
{
let num_ref: &mut i32 = &mut foo[2]; // Take a reference to an item in the vec
*num_ref = 12; // Modify the item pointed to by num_ref
}
println!("{:?}", foo);
}
上面的代码将打印出来:
[1, 2, 3, 4, 5]
[1, 2, 12, 4, 5]
所以不幸的是,在这种情况下无法使用解除引用。但是你很幸运 - 这是解决你的类型不匹配问题的简单方法。方便特性Clone
定义了一个名为clone()
的函数,该函数需要创建一个具有相同值的全新类型实例。 Rust实现Clone
中的大多数基本类型,包括String
。因此,通过单个函数调用,您的类型不匹配问题就会消失:
fn select_lines(pattern: &String, lines: &Vec<String>) -> Vec<String> {
let mut selected_lines: Vec<String> = Vec::new();
for line in lines {
if line.contains(pattern) {
// 'line.clone()' will create a new String for
// each line that contains the pattern, and
// place it into the vector.
selected_lines.push(line.clone());
}
}
selected_lines
}
clone()
是你的朋友,你应该熟悉它,但要注意它确实需要额外的内存(如果所有行都匹配,最多可以加倍),并且行放入{{1 }}无法轻易链接回selected_lines
中的对应者。在你离开实验并使用非常大的数据集进入生产之前,你不应该担心内存问题,但如果后一个问题出现问题,我想指出这个替代解决方案,编辑函数签名,以便将引用返回到匹配的行:
lines
此示例包含Lifetimes,这是您可能不会需要学习一段时间的内容,但您可以随意查看此示例!
Playground link, with an example, and some mutability edits.