Your implementation slightly deviates from the standard definition of Cons lists. The straight-forward definition (similar to how you would do it in Lisp) is
type Data = isize;
enum List {
Nil,
Cons(Box<List>, Data),
}
As you can see, the Cons
variant is made up of the nested list and this nodes data element. In your case, each node has an isize
.
If you have an Arc<List>
or Box<List>
, this nested List
object could be the Cons
variant too and carry another isize
. Arc
and Box
don't care about what they are pointing to.
There are some things that are not quite idiomatic. Having both the Tail
and Nil
variant doesn't make much sense, because now you have two ways to signal the end of the list. Similarly, having the Head
be a variant of the list is strange, because the head of the list is only at the beginning, your implementation allows for a Head
variant in the middle of the list though.
It is preferable to not have an extra Nil
node to signal the end of the list. Instead, the last node knows that it is the last (like your Tail
variant) so that you don't have an extra allocation for the empty node. This is what I would perceive as an idiomatic definition of a singly linked list in Rust:
struct List {
// ...
// extra info for list head
// ...
first_node: Option<Box<ListNode>>,
}
struct ListNode {
value: Data,
next: Option<Box<ListNode>>,
}
To make this generic, we simply have to remove our type alias Data
from earlier and make it a generic parameter instead:
struct ListNode<T> {
value: T,
next: Option<Box<ListNode<T>>>,
}
About Box
and Arc
: Box
is an owned pointer to a value on the heap, that means only this one box owns the memory it points to. Arc
is a thread safe version of Rc
, which is a reference-counted heap value. Multiple Rc
-pointers to this memory can exist and read it, the number of these is counted. Thus, the ownership may be shared.
You should pick which to use, based on whether or not you want to create more reference-counted pointers to the nodes (note that you probably want either Rc<RefCell<Node>>
or Arc<Mutex<Node>>
to also mutate the list after creating it). If you only ever have the head of the list and want to iterate over it, pick Box
instead.