I have a Cons list:
#[derive(Debug)]
pub enum Cons {
Empty,
Pair(i64, Box<Cons>),
}
I want to implement FromIterator<i64>
for this type, in an efficient manner.
Attempt one is straightforward: implement a push
method which recursively traverses the list and transforms a Cons::Empty
into a Cons::Pair(x, Box::new(Cons::Empty))
; repeatedly call this push
method. This operation is O(n^2) in time and O(n) in temporary space for the stack frames.
Attempt two will combine the recursion with the iterator to improve the time performance: by pulling a single item from the iterator to construct a Cons::Pair
and then recursing to construct the remainder of the list, we now construct the list in O(n) time and O(n) temporary space:
impl FromIterator<i64> for Cons {
fn from_iter<I>(iter: I) -> Self
where
I: IntoIterator<Item = i64>,
{
let mut iter = iter.into_iter();
match iter.next() {
Some(x) => Cons::Pair(x, Box::new(iter.collect())),
None => Cons::Empty,
}
}
}
In C, it would be possible to implement this method using O(n) operations and O(1) working space size. However, I cannot translate it into Rust. The idea is simple, but it requires storing two mutable pointers to the same value; something that Rust forbids. A failed attempt:
impl FromIterator<i64> for Cons {
fn from_iter<I>(iter: I) -> Self
where
I: IntoIterator<Item = i64>,
{
let mut iter = iter.into_iter();
let ret = Box::new(Cons::Empty);
let mut cursor = ret;
loop {
match iter.next() {
Some(x) => {
let mut next = Box::new(Cons::Empty);
*cursor = Cons::Pair(x, next);
cursor = next;
}
None => break,
}
}
return *ret;
}
}
error[E0382]: use of moved value: `next`
--> src/lib.rs:20:30
|
18 | let mut next = Box::new(Cons::Empty);
| -------- move occurs because `next` has type `Box<Cons>`, which does not implement the `Copy` trait
19 | *cursor = Cons::Pair(x, next);
| ---- value moved here
20 | cursor = next;
| ^^^^ value used here after move
error[E0382]: use of moved value: `*ret`
--> src/lib.rs:25:16
|
13 | let ret = Box::new(Cons::Empty);
| --- move occurs because `ret` has type `Box<Cons>`, which does not implement the `Copy` trait
14 | let mut cursor = ret;
| --- value moved here
...
25 | return *ret;
| ^^^^ value used here after move
Is it possible to perform this algorithm in safe Rust? How else could I implement an efficient FromIterator
for my Cons
type? I understand that I may be able to make some headway by switching Box
to Rc
, but I'd like to avoid this if possible.