-
Notifications
You must be signed in to change notification settings - Fork 2k
Description
I think this is not correct.
I wrote this debugging code:
#[derive(Debug)]
enum List<T> {
/// A non-empty list: first element and the rest of the list.
Element(T, Box<List<T>>),
/// An empty list.
Nil,
}
fn main() {
let list: List<i32> = List::Element(1, Box::new(List::Element(2, Box::new(List::Nil))));
let list2: List<i32> = List::Element(1, Box::new(List::Element(2, Box::new(List::Nil))));
let list3: List<i32> = List::Element(1, Box::new(List::Element(2, Box::new(List::Nil))));
println!("{list:?}");
unsafe {
let first: u128 = std::mem::transmute(list);
println!("{first:#034x}");
let List::<i32>::Element(_, cdrbox) = list2 else {
panic!("impossible");
};
let second: u128 = std::mem::transmute(*cdrbox);
println!("{second:#034x}");
let List::<i32>::Element(_, cdrbox3) = list3 else {
panic!("impossible");
};
let List::<i32>::Element(_, cddrbox) = *cdrbox3 else {
panic!("impossible");
};
let third: u128 = std::mem::transmute(*cddrbox);
println!("{third:#034x}");
}
}
What I'm trying to do here, is to get the representation out of Rust in all 3 steps of the linked list, and this is the output I have:
errge:~/rust $ ./target/release/box
Element(1, Element(2, Nil))
0x0000564fac517bc00000000100000000
0x0000564fac517be00000000200000000
0x0000564fac517be00000000200000001
errge:~/rust $ qemu-aarch64 ./target/aarch64-unknown-linux-musl/release/box
Element(1, Element(2, Nil))
0x00000055008068500000000100000000
0x00000055008068700000000200000000
0x00000055008068700000000200000001
errge:~/rust $ ./target/x86_64-unknown-linux-musl/release/box
Element(1, Element(2, Nil))
0x00007fa1c7b688500000000100000000
0x00007fa1c7b688700000000200000000
0x00007fa1c7b688700000000200000001
What we see here (on all 3 architectures), is that the first and second list element has an enum discriminant of (0x0), while the third one has a discriminant of (0x1).
The discriminants are at the end of the printout, because all 3 architectures are little endian.
And I also have a theoretical reasoning why the provided picture for the niche optimization is not possible: linked lists are famous for being a data structure, where you can return a reference to the middle and users can continue to walk the linked list from there, or maybe even share data from there, etc. Imagine how would we implement a find() method for this linked list? It would probably return List<T>, the first item that matched the search criteria, and the caller than can even see the items behind the found item. Now, if there are no results found, we have to return Nil, which is at the end of the list anyway, so we can just return that Nil. But if the niche optimization were really to happen the way how the slide explains, then that final closing Nil element would not be there.
I'm reporting an issue here instead of a pull request, because I'm only 95% sure, and I'm happy to do the work, but wanted to hear confirmation first from someone more experienced. @djmitche Do you have an opinion about this too?