I have the following Rust MCVE:
#[derive(Clone)]
struct X {
x: i32,
}
fn fold1<I: Iterator, F>(mut it: I, f: F) -> I::Item
where
I::Item: Clone,
F: FnMut(I::Item, I::Item) -> I::Item,
{
let init = it.next().unwrap().clone();
it.fold(init, f)
}
fn main() {
let v = vec![X { x: 1 }, X { x: 2 }];
// This does not compile:
let s = fold1(v.iter(), |a, b| X { x: a.x + b.x });
println!("{}", s.x);
}
This doesn't compile with the following error:
error[E0308]: mismatched types
--> example.rs:19:36
|
19 | let s = fold1(v.iter(), |a, b| X { x: a.x + b.x });
| ^^^^^^^^^^^^^^^^^^ expected `&X`, found `X`
|
help: consider borrowing here
|
19 | let s = fold1(v.iter(), |a, b| &X { x: a.x + b.x });
| +
error: aborting due to previous error
For more information about this error, try `rustc --explain E0308`.
However, if I instead manually inline the function:
#[derive(Clone)]
struct X {
x: i32,
}
fn main() {
let v = vec![X { x: 1 }, X { x: 2 }];
// The inlined version, however, does:
let mut it = v.iter();
let init = it.next().unwrap().clone();
let s = it.fold(init, |a, b| X { x: a.x + b.x });
println!("{}", s.x);
}
then everything is fine. The code compiles and does what was intended.
I really can't see any difference here. What am I doing wrong?
(And, as a followup, is it possible to write a function like fold1
that would clone the first element of the iterator and then use it as the initial element for fold
? No, reduce
is not the answer. Calling cloned()
on the iterator is not the answer either. I really want to only clone the first one, not all of them.)
In your working code, you are calling fold::<X, FnMut (X, &X) -> X>
but in the non-working case, you try to call fold::<&X, FnMut (&X, &X) -> &X>
, since I::Item
is &X
. This can't work because the return type of your closure is not the same as the initial fold
value. You need to change the declaration of fold1
so that it will match the working code:
fn fold1<'a, T, I: Iterator<Item = &'a T>, F> (mut it: I, f: F) -> T
where
T: Clone + 'a,
F: FnMut (T, &'a T) -> T,
{
let init = it.next().unwrap().clone();
it.fold (init, f)
}