7 ms·
When people asking my opinion for Rust, I loved to share them the Linkedin List implementation link: https://doc.rust-lang.org/src/alloc/collections/linked_list
by adeptima 4y ago
When people asking my opinion for Rust, I loved to share them the Linkedin List implementation link:
https://doc.rust-lang.org/src/alloc/collections/linked_list.rs.html#158 https://doc.rust-lang.org/src/alloc/collections/linked_list....
And the first feedback is why so many unsafe blocks?
What is it Option<NonNull<Node<T>>> ?
'_ ?
Another reason to share, if you can understand Linkedin List, you are free to code in Rust ;)
- SighMagi 4y agoThere’s so much code marked “unsafe” in there…
- tracker1 4y agoThere be dragons...
- estebank 4y agoThat's because in order to interact with pointers you need unsafe, and not using unsafe in Rust requires a single ownership tree or reference counting. If you're not keen on either of those solutions, you shouldn't be writing a Doubly Linked List in other languages either (because they will have either pointers or reference counting/GC).
- andrewflnr 4y ago"Those solutions" start off being hierarchical ownership or reference counting, but turn into "pointers or reference counting". Requiring pointers is implicit to the definition of linked lists, and calling it out here is silly. And you don't really need either ref counting (again, pretty obviously, witness every real world implementation) or hierarchical ownership outside Rust. Conceptually, the list as a vague concept owns all the nodes. The ownership just stops being expressible directly through references the way Rust wants it to be. But you don't have to re-invent hierarchical ownership to implement DLLs in other languages, because it's not really there in the problem. You just have to accept that they're always going to suck under Rust's ownership model.
- estebank 4y ago> Requiring pointers is implicit to the definition of linked lists, and calling it out here is silly. The usual complaints are that "Safe Rust doesn't let you do this", when unsafe Rust is right there to let you operate on any soup of pointers datastructure that you'd want to implement. > you don't really need either ref counting (again, pretty obviously, witness every real world implementation) If you implement DDL in a memory managed language, you effectively have the same behavior as you would in Rust with Arc or Rc. The ownership of the nodes then belongs to the GC or the RC value. You don't have to think about it because you're abstracted from the lifetime of each node by the language and runtime. > or hierarchical ownership outside Rust The ownership/borrow checker requires hierarchical to operate, but it doesn't stop you from writing unsafe Rust. > Conceptually, the list as a vague concept owns all the nodes. The ownership just stops being expressible directly through references the way Rust wants it to be. But you don't have to re-invent hierarchical ownership to implement DLLs in other languages, because it's not really there in the problem. You just have to accept that they're always going to suck under Rust's ownership model. Yeah, and that's what unsafe is for. Most code doesn't need vague ownership, though. I'd go as far as saying that the nudge towards clear ownership helps both maintainability and performance. My concern is with the prevalence of this idea that unsafe Rust is not "real Rust" or that the existence and use of unsafe Rust somehow precludes any benefits of Rust.
- db48x 4y agoTechnically the pointers don’t have to be actual pointers. And if they’re not actually pointers, then Rust’s ownership model and borrow checker will be perfectly content; they’ll barely trouble you at all. And you won’t even need any unsafe blocks. What you do instead is use integers instead of pointers. The integers are indexes into a Vec of list nodes, owned by the linked list. Since the nodes are now owned only by the Vec, which is in turn owned only by the linked list, the borrow checker will not complain. Some people object that this is cheating somehow, but what is memory but a giant untyped global vector, shared between all parts of your application? Pointers into that giant shared array are just indexes with extra risk, since you have to trust that they point to real nodes that have been initialized. Plus, you often see users of a linked list put the nodes into an arena allocator anyway, especially in the kernel. The Vec in your Rust implementation serves the same purpose as the arena allocator.
- hnov 4y agoIsn't a doubly linked list basically incompatible with Rust's idea of memory ownership?
- proto_lambda 4y agoIt's definitely not a data structure that makes the borrow checker happy. Luckily it's also not a data structure that's required or desirable in 99% of cases, but the standard library still offers an implementation for those 1% so people don't try to write their own (turns out getting a doubly linked list correct isn't quite as trivial as it may seem).
- Waterluvian 4y agoI’m actually a bit surprised it’s in the standard library if it’s so uncommonly needed. Isn’t Rust’s stdlib tiny with a philosophy of letting the community fill in most things?
- estebank 4y agoThere were arguments made for its non-inclusion[1]. [1]: https://rust-unofficial.github.io/too-many-lists/sixth.html https://rust-unofficial.github.io/too-many-lists/sixth.html
- Waterluvian 4y agoI enjoyed reading this. A fun style.
- tmtvl 4y agoFrom what I remember Rust has some kind of RefCell with weak references. Those could be used to make DLLs, if anyone can find a reason to use those. That said, you could also use zippers...
- slaymaker1907 4y ago
- dekhn 4y agoI don't know Rust but I would guess that an Option<NonNull<Node<T>>> would be a Node of type T, that cannot be set to null, but can be optional. This is a type I would want to return from a function that could either return nothing, or a pointer to a real thing. As for the unsafety, I would assume the authors of collections know what they are doing and do that for performance.
- proto_lambda 4y ago`NonNull` is actually a wrapper around the pointer type, with the added invariant that the pointer cannot be null. The compiler is made aware of this, which allows `Option<NonNull<T>>` to be just a plain pointer, where the `None` variant of the option corresponds to a null pointer and the `Some(ptr)` case corresponds to a non-null pointer.
- josephg 4y agoI really wish rust had better syntax for this. Raw C pointers should probably all be Option<NonNull<T>> rather than *mut T. The latter is easier to type but worse in almost every way. Ergonomics should guide you to the former, not the latter.
- andrewflnr 4y agoI think if you tried to do that, you'd basically be mixing type-driven optimizations with the C FFI, which sounds sketchy, to me at least. The null-optimization for Option is just that, an optimization, and I don't like taking it for granted where safety/security is at stake.
- josephg 4y agoThats fair. I suppose one problem with this approach for C FFI is that there's a lot of different values which could all be "null pointers". Converting them all for Option would be awkward and slow, and you wouldn't want to ever risk getting this stuff wrong. But pointers are also useful even if you aren't doing FFI. Eg for implementing custom data structures. In that case, Option<NonNull<T>> (Or even NonNull<T>) is usually better than *mut T. But its harder to type, and it doesn't clearly tell you if the pointer should be *mut T or *const T. NonNull<T> should be preferred because security/safety is at stake for this sort of code.
- chlorion 4y agoConsidering that unsafe rust basically just allows raw pointer access (see link below) which is already a thing you can do normally in C, I do not see how that's a very good argument against it honestly. As for the Option type, it is exactly what it says. It's an optional, non-nullable node generic over type T. I suppose generics, optionals and the idea of non-nullable types might be exotic at first, but this is not a problem with Rust as much as it's a problem with C not being able to express these concepts in the first place and instead expecting you to spray null checks all over the place! https://doc.rust-lang.org/book/ch19-01-unsafe-rust.html https://doc.rust-lang.org/book/ch19-01-unsafe-rust.html
- scaramanga 4y agoNot to be overly contrary here but, you "need to spray null checks" probably just as much in rust as in C (in this specific example, not in general). Since those prev/next pointers are being modified in unsafe code where either you're using NonNull::new() which contains the null check, or you're using NonNull::new_unchecked() which means you need to convince yourself all the invariants hold true. The situation is roughly equal in C (ie. once you prove in a module that fields cannot be NULL, no need to add lots of redundant extra NULL checks in all users of the module).
- kibwen 4y agoWhen explaining why the difficulty of modeling linked lists in Rust doesn't matter, I like to share the following book, "Learn Rust With Entirely Too Many Linked Lists", written by the woman who wrote the implementation you've linked above: https://rust-unofficial.github.io/too-many-lists/ https://rust-unofficial.github.io/too-many-lists/
- keepquestioning 4y agoI read this and noped out of learning Rust.
- kelnos 4y agoSo I glanced at it, expecting something much much worse, but I was surprised to see it wasn't that bad. Even if you look at the final code for the final implementation, it doesn't seem much more complex than what you'd have to write in C. And a good chunk of the code is implementing traits from the stdlib, stuff that isn't strictly necessary, but makes it easier to make the API of the deque idiomatic and work well with other stdlib (and non-stdlib) APIs. And regardless, this book is teaching from the perspective of intentional super-hard-mode. Most Rust developers will never have to write that much unsafe code.
- hinkley 4y agoAre you bragging about self imposed ignorance of advanced programming topics? I guess there’s plenty of space in the world for PHP programmers and their predecessors, the VB programmers. So… congrats?
- adeptima 4y agoMy first thought it was a joke ... ;) Learn Rust with entirely too many linked lists (2019) - https://news.ycombinator.com/item?id=22390662 https://news.ycombinator.com/item?id=22390662 - https://rust-unofficial.github.io/too-many-lists/index.html https://rust-unofficial.github.io/too-many-lists/index.html In this series I will teach you basic and advanced Rust programming entirely by having you implement 6 linked lists. In doing so, you should learn: - The following pointer types: &, &mut, Box, Rc, Arc, const, mut, NonNull(?) - Ownership, borrowing, inherited mutability, interior mutability, Copy - All The Keywords: struct, enum, fn, pub, impl, use, ... - Pattern matching, generics, destructors - Testing, installing new toolchains, using miri - Unsafe Rust: raw pointers, aliasing, stacked borrows, UnsafeCell, variance
- andrewflnr 4y agoIn the Rust ecosystem philosophy, linked lists are the kind of thing you want to write once, very carefully, then re-use a lot. That's exactly the kind of code where unsafe can be reasonable. It's not like it would actually be safer in any other language that didn't have an explicit unsafe keyword. More aesthetically pleasing, perhaps, but not safer. You're acting like it's some epic burn on Rust that there's some ugly code in its stdlib. It's not. Stdlibs are like that. Furthermore, as others have pointed out, linked lists in particular are a pathological case for Rust, meaning you're pointing and snickering at a special case of a special case. Most people never have to write or look at that kind of code.
- svnpenn 4y agothis comes off as extremely defensive. I don't feel like the person you're responding to, intended any kind of "sick burn". Calm down dude.
- andrewflnr 4y agoWhatever. They're definitely pointing and snickering. I don't think there's any way to read their post but as a criticism of Rust the language based on this one piece of stdlib code, and that's nonsense. That irritates me. So sue me. There are reasonable arguments to be had about Rust. I wish people would do those instead of all the nonsense you usually see.
- svnpenn 4y agoon the contrary, I think more and more people are unwilling to hear any criticism, even constructive criticism, of "their thing". Rust is not perfect. I wont go into its faults, but you know what they are. I think overall, Rust is a great language, but you have to be realistic and understand that some stuff you ignore in favor of the holistic view, others might not be able to. So sometimes maybe just take the criticism and deal with it, or if you're in the position, do something to fix the problem.
- 4y ago
- nemothekid 4y ago>When people asking my opinion for Rust, I loved to share them the Linkedin List implementation link: This LinkedList obsession is a bit bizarre to me, and tends to come from older programmers who come from a time when coding interviews involved writing linked lists and balancing b-trees. To me though it also represents the stubbornness of C programmers who refuse to consider things like growable vectors a solved problem. My reaction to the LinkedList coders is not "well Rust needs to maintain ownership", its why does your benchmark for how easy a language is involve how easy it is to fuck around with raw pointers?. LinkedLists are a tool, but to C programmers that are an invaluable fundamental building block that shows up early in any C programmers education due to how simple they are to implement and the wide range of use cases they can be used for. But they are technically an unsafe data structure and if you willing to let some of that stubbornness go and finally accept some guard rails, you have to be able to see that a data structure like linkedlists will be harder to implement. It has nothing to do with the language; implementing with LinkedLists with any sort of guardrails adds a ton of complexity, either up front (e.g. borrowchecker) or behind the scenes (e.g. a garbage collector). When you accept this fact, it becomes ludicrous to imply that a LinkedList implementation is a good benchmark for the ergonomics of a language like Rust.
- bjourne 4y agoWtf? That code looks like shit.
- mastax 4y agoC++'s STL linked list for comparison (libcxx). https://github.com/llvm-mirror/libcxx/blob/master/include/list https://github.com/llvm-mirror/libcxx/blob/master/include/li...
- kortex 4y agoOof. Yeah, standard lib code tends to be harder to read on average than application code. But at least I can mentally parse the Rust code. Heck it even looks like a linked list impl. The c++ code looks barely better than line noise. I'm sure I could wrap my head around it, but ho boy, it's far worse than Rust by a long shot. And this is with several years cpp experience, and maybe a few months of Rust on and off.
- einpoklum 4y agoRust's list only supports a default allocation mechanism. For an actual comparison, you'll want to show us a Rust implementation which is parametrized over allocators. Having said that - yes, it's not pretty. About the same length though.
- mastax 4y agoAdding allocator support does add a bit more noise: https://github.com/rust-lang/rust/pull/103093/files#diff-8bd1153cc6d89a3ef4bcd62045ed29b00fc7ddc4a0937d276b9e17a2ffc2772d https://github.com/rust-lang/rust/pull/103093/files#diff-8bd...
- riwsky 4y agoAlso the implementation is all wrong. The proper way to add an element to a linkedin list is to send the element an email titled "I just requested to connect", along with a "view profile" and "accept" link.
- xdavidliu 4y ago> I loved to share them the Linkedin List implementation ... if you can understand Linkedin List ... is the "In" in "LinkedIn" deliberate here or just a typo that was made twice?