如果累加器既不是复制类型也不是可变的,是否可以在 Rust 中使用 fold
Is it possible to use fold in Rust if the accumulator is neither copy type nor mutable
我正在尝试折叠工作并遇到了生命周期问题。这是我的代码的简化版本:
struct A {
v: i32,
}
let v = vec![A { v: 1 }, A { v: 2 }, A { v: 3 }];
let max = v.iter().fold(None, |res: Option<A>, &item| { match res {
Some(a) => if a.v >= item.v { Some(a) } else { Some(item) },
None => Some(item)
}});
这导致 error[E0507]: cannot move out of borrowed content
。
如果我按照上一个错误的建议将闭包更改为 |res: Option<A>, ref item| ...
,我会在 Some(item)
处遇到类型不匹配,解决该取消引用会返回到原始错误。
我试图在选项中有一个参考,但后来我遇到了生命周期问题:
error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
--> <anon>:6:67
|
6 | let maxV = v.iter().fold(None, |res: Option<&A>, &item| { match res {
| ^^^
|
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the body at 6:58...
--> <anon>:6:59
|
6 | let maxV = v.iter().fold(None, |res: Option<&A>, &item| { match res {
| ___________________________________________________________^ starting here...
7 | | Some(a) => if a.v >= item.v { Some(a) } else { Some(&item) },
8 | | None => Some(&item)
9 | | }});
| |____^ ...ending here
note: ...so that types are compatible (expected std::option::Option<&main::A>, found std::option::Option<&main::A>)
--> <anon>:6:67
|
6 | let maxV = v.iter().fold(None, |res: Option<&A>, &item| { match res {
| ^^^
note: but, the lifetime must be valid for the method call at 6:13...
--> <anon>:6:14
|
6 | let maxV = v.iter().fold(None, |res: Option<&A>, &item| { match res {
| ______________^ starting here...
7 | | Some(a) => if a.v >= item.v { Some(a) } else { Some(&item) },
8 | | None => Some(&item)
9 | | }});
| |_____^ ...ending here
note: ...so that argument is valid for the call
--> <anon>:6:28
|
6 | let maxV = v.iter().fold(None, |res: Option<&A>, &item| { match res {
| ^^^^
如果我通过添加 #[derive (Copy, Clone)]
使 A 成为复制类型,第一个版本就可以工作,但这并不总是一个选项。
我试图在 Rust 中搜索折叠示例,但我要么找到了累加器是复制类型(i32 总和使用折叠实现),要么累加器是容器并且折叠正在操纵内容(扩展矢量或类似物)。
我也找到了 an example that folds into an Option,但它与累加器的当前值不匹配。
我可以使用 for 循环来完成,但更喜欢折叠语法。
您从 Vec::iter()
获得的迭代器产生对向量中元素的不可变借用引用(在本例中为 &A
);之后矢量将保持不变。
提醒一下,默认情况下在 Rust 中使用值 moves it out,使得原始值无法使用;如果您拥有该项目,您只能这样做,即您不能搬出借来的参考资料。例外情况是 Copy
类型,这意味着它们 "simple enough" 原始内存副本是可以的并且不会使原始对象无效。
因此对于任何不是 Copy
的类型,您不能直接从不可变引用进行赋值。
有几个选项。
首先,如果您可以在没有 Copy
的情况下实现或派生 Clone
(这意味着您提供了一种可以复制对象的方法,可能不仅仅是原始副本),您可以显式克隆:
let max = v.iter().fold(None, |res: Option<A>, item| match res {
Some(a) => if a.v >= item.v { Some(a) } else { Some(item.clone()) },
None => Some(item.clone()),
});
您可以手动构造内联的新项目 (Some(A { v: item.v })
),但如果您这样做,您也可以派生或实施 Clone
.
如果你可以消耗(即销毁)你的项目向量来获得结果,那么你毕竟可以通过调用 into_iter()
方法而不是 iter()
来移动;在这种情况下,迭代器拥有项目(原始所有者 Vec
在此过程中被消耗)因此您可以移动它们:
let max = v.into_iter().fold(None, |res: Option<A>, item| match res {
Some(a) => if a.v >= item.v { Some(a) } else { Some(item) },
None => Some(item),
});
移动到那里没问题,但是 Vec
不存在了。
无论哪种方式,您都需要制作副本(通过 Copy
、Clone
或更手动的方式)或移动 - 这取决于您的应用程序,哪个更合适。
编译器中存在几个关于生命周期参数对闭包参数的推断的错误;您在这里遇到的那个被报告为 issue 36867。在 res: Option<&A>
中,为 &A
推断的生命周期是错误的,这似乎是由 return 类型(即 Option<&A>
)也有一个生命周期参数引起的。解决方法是让编译器推断 整个类型 ,这工作正常,而是在初始累加器值 (None
) 上给出类型提示:
#[derive(Debug)]
struct A {
v: i32,
}
fn main() {
let v = vec![A { v: 1 }, A { v: 2 }, A { v: 3 }];
let max = v.iter().fold(None::<&A>, |res, item| { match res {
Some(a) => if a.v >= item.v { Some(a) } else { Some(item) },
None => Some(item)
}});
println!("{:?}", max);
}
或 max
变量:
fn main() {
let v = vec![A { v: 1 }, A { v: 2 }, A { v: 3 }];
let max: Option<&A> = v.iter().fold(None, |res, item| { match res {
Some(a) => if a.v >= item.v { Some(a) } else { Some(item) },
None => Some(item)
}});
println!("{:?}", max);
}
奖励: 对于寻找最大值的特定问题,您也可以使用 Iterator::max_by_key
。无需注释!
fn main() {
let v = vec![A { v: 1 }, A { v: 2 }, A { v: 3 }];
let max = v.iter().max_by_key(|item| item.v);
println!("{:?}", max);
}
我正在尝试折叠工作并遇到了生命周期问题。这是我的代码的简化版本:
struct A {
v: i32,
}
let v = vec![A { v: 1 }, A { v: 2 }, A { v: 3 }];
let max = v.iter().fold(None, |res: Option<A>, &item| { match res {
Some(a) => if a.v >= item.v { Some(a) } else { Some(item) },
None => Some(item)
}});
这导致 error[E0507]: cannot move out of borrowed content
。
如果我按照上一个错误的建议将闭包更改为 |res: Option<A>, ref item| ...
,我会在 Some(item)
处遇到类型不匹配,解决该取消引用会返回到原始错误。
我试图在选项中有一个参考,但后来我遇到了生命周期问题:
error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
--> <anon>:6:67
|
6 | let maxV = v.iter().fold(None, |res: Option<&A>, &item| { match res {
| ^^^
|
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the body at 6:58...
--> <anon>:6:59
|
6 | let maxV = v.iter().fold(None, |res: Option<&A>, &item| { match res {
| ___________________________________________________________^ starting here...
7 | | Some(a) => if a.v >= item.v { Some(a) } else { Some(&item) },
8 | | None => Some(&item)
9 | | }});
| |____^ ...ending here
note: ...so that types are compatible (expected std::option::Option<&main::A>, found std::option::Option<&main::A>)
--> <anon>:6:67
|
6 | let maxV = v.iter().fold(None, |res: Option<&A>, &item| { match res {
| ^^^
note: but, the lifetime must be valid for the method call at 6:13...
--> <anon>:6:14
|
6 | let maxV = v.iter().fold(None, |res: Option<&A>, &item| { match res {
| ______________^ starting here...
7 | | Some(a) => if a.v >= item.v { Some(a) } else { Some(&item) },
8 | | None => Some(&item)
9 | | }});
| |_____^ ...ending here
note: ...so that argument is valid for the call
--> <anon>:6:28
|
6 | let maxV = v.iter().fold(None, |res: Option<&A>, &item| { match res {
| ^^^^
如果我通过添加 #[derive (Copy, Clone)]
使 A 成为复制类型,第一个版本就可以工作,但这并不总是一个选项。
我试图在 Rust 中搜索折叠示例,但我要么找到了累加器是复制类型(i32 总和使用折叠实现),要么累加器是容器并且折叠正在操纵内容(扩展矢量或类似物)。
我也找到了 an example that folds into an Option,但它与累加器的当前值不匹配。
我可以使用 for 循环来完成,但更喜欢折叠语法。
您从 Vec::iter()
获得的迭代器产生对向量中元素的不可变借用引用(在本例中为 &A
);之后矢量将保持不变。
提醒一下,默认情况下在 Rust 中使用值 moves it out,使得原始值无法使用;如果您拥有该项目,您只能这样做,即您不能搬出借来的参考资料。例外情况是 Copy
类型,这意味着它们 "simple enough" 原始内存副本是可以的并且不会使原始对象无效。
因此对于任何不是 Copy
的类型,您不能直接从不可变引用进行赋值。
有几个选项。
首先,如果您可以在没有 Copy
的情况下实现或派生 Clone
(这意味着您提供了一种可以复制对象的方法,可能不仅仅是原始副本),您可以显式克隆:
let max = v.iter().fold(None, |res: Option<A>, item| match res {
Some(a) => if a.v >= item.v { Some(a) } else { Some(item.clone()) },
None => Some(item.clone()),
});
您可以手动构造内联的新项目 (Some(A { v: item.v })
),但如果您这样做,您也可以派生或实施 Clone
.
如果你可以消耗(即销毁)你的项目向量来获得结果,那么你毕竟可以通过调用 into_iter()
方法而不是 iter()
来移动;在这种情况下,迭代器拥有项目(原始所有者 Vec
在此过程中被消耗)因此您可以移动它们:
let max = v.into_iter().fold(None, |res: Option<A>, item| match res {
Some(a) => if a.v >= item.v { Some(a) } else { Some(item) },
None => Some(item),
});
移动到那里没问题,但是 Vec
不存在了。
无论哪种方式,您都需要制作副本(通过 Copy
、Clone
或更手动的方式)或移动 - 这取决于您的应用程序,哪个更合适。
编译器中存在几个关于生命周期参数对闭包参数的推断的错误;您在这里遇到的那个被报告为 issue 36867。在 res: Option<&A>
中,为 &A
推断的生命周期是错误的,这似乎是由 return 类型(即 Option<&A>
)也有一个生命周期参数引起的。解决方法是让编译器推断 整个类型 ,这工作正常,而是在初始累加器值 (None
) 上给出类型提示:
#[derive(Debug)]
struct A {
v: i32,
}
fn main() {
let v = vec![A { v: 1 }, A { v: 2 }, A { v: 3 }];
let max = v.iter().fold(None::<&A>, |res, item| { match res {
Some(a) => if a.v >= item.v { Some(a) } else { Some(item) },
None => Some(item)
}});
println!("{:?}", max);
}
或 max
变量:
fn main() {
let v = vec![A { v: 1 }, A { v: 2 }, A { v: 3 }];
let max: Option<&A> = v.iter().fold(None, |res, item| { match res {
Some(a) => if a.v >= item.v { Some(a) } else { Some(item) },
None => Some(item)
}});
println!("{:?}", max);
}
奖励: 对于寻找最大值的特定问题,您也可以使用 Iterator::max_by_key
。无需注释!
fn main() {
let v = vec![A { v: 1 }, A { v: 2 }, A { v: 3 }];
let max = v.iter().max_by_key(|item| item.v);
println!("{:?}", max);
}