Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

> That's a non sequitur.

Why not? An LRU cache is moderately complex specialized data structure. As you see, for some reasons, it’s much simpler to implement in C++ then in Rust.

> The choice made by thee stdlib to not support many linked list operations does not imply Rust is unsuitable for complex specialized data structures

I never said it’s unsuitable. Technically, all modern programming languages are Turing complete, so in some sense they’re equivalent. I only said Rust isn’t good enough for that. Meaning, in many other languages doing that is much simpler than in Rust.

Linked lists are special case of trees, which in turn special case of graphs. Both trees and graphs are used a lot: DOM trees, syntax trees, KD/PK trees, quad edge graphs, and so on.

Rust’s memory management model is neither manual nor garbage collected. This feature makes it hard to implement those pointer-based data structures, where items are in shared ownership of the container, where you want to split/merge those containers, and move items across containers.

Not impossible, just hard and/or inefficient (like those ref.counted implementation that increment/decrement those counters each time I traverse the graph, even if I only reading stuff).



> it’s much simpler to implement in C++ then in Rust.

Because of the standard library. This is not a language issue, this is a library issue. This has nothing to do with Rust's memory model. It has to do with Rust's choices of having a smaller, maintainable stdlib and relying on the crates ecosystem for most libraries (which can then evolve independently of the language, which is nice).

Someone has to write a linkedlist implementation of the type you describe and put it in a crate, and that's it; everyone can use that. It should be trivial to make a pull request to https://github.com/contain-rs/linked-list to add the method you describe, for example.

--------

Edit:

Rust does make it harder to implement the bottom level of low level abstractions (doubly linked lists, LRU caches, etc), since these deal with memory the most and can easily become unsafe. You implement them once with a safe API, and use them as much as you want.

The reason std doesn't expose too many unsafe APIs on its datastructures is because if you need that level of control, it is better to have your own implementation. Safety is hard; and the more unsafe APIs you expose, the more you run the risk of external unsafe code depending on the internals of the implementation in the stdlib. Due to this, it would be preferred if you write the LRU cache with a linkedlist implementation embedded in it (which could be a separate crate for reusability), with the invariants well documented. This is a choice made by the stdlib, but has nothing to do with the language itself and is a choice that external crates are free to not follow.


> Rust does make it harder to implement the bottom level of low level abstractions

Yeah, prohibitively harder. If you really need an efficient linked list, or tree, or graph, Rust makes it nearly impossible to accomplish.

> The reason std doesn't expose too many unsafe APIs on its datastructures is because if you need that level of control, it is better to have your own implementation

This might be true for Rust, but the argument is language-specific. In C++ I often need “that level of control” and yet I’m happy with many stock implementations. Moreover, I can combine different data structures to produce whatever I need. Can’t in Rust, because the library designers (not just std, the rest of them as well) think I better have my own implementation if I need that level of control.

> Safety is hard

Not necessarily. C# is very safe, and yet it’s much friendlier to developers: I never used unsafe C# code to overcome language limitations, only to interop with unmanaged code.


> Rust makes it nearly impossible to accomplish.

No it doesn't. It's literally adding one extra simple function to a library.

> Not necessarily.

Well-performing safety is hard, then. Safety is hard in C++ and Rust, less so in C#. Yes, it is easier to write a linked list in C++, but ensuring it is used safely is hard.

> Can’t in Rust, because the library designers (not just std, the rest of them as well) think I better have my own implementation if I need that level of control.

Right, and that is solely a library issue. Rust doesn't prohibit having composable unsafe libraries either. The ecosystem just hasn't produced those yet, because people probably didn't need it yet; and it is mostly preferred for libraries to keep the unsafety encapsulated inside behind a safe abstraction.


> It's literally adding one extra simple function to a library.

No function will transfer ownership of N nodes to the new container for O(1) complexity.

And if you’ll refactor the list to reference counted nodes, you’ll introduce costs to traversal.

> it is easier to write a linked list in C++

And a tree. And a graph. Trees / graphs structures and algorithms are everywhere, too bad Rust language designers forgot that.

> Rust doesn't prohibit having composable unsafe libraries either.

Does not prohibit but makes it hard, because Rust’s primary design goal was that safety thing.


> No function will transfer ownership of N nodes to the new container for O(1) complexity.

No safe function. It is easy to do with unsafe. This has been explained already.

> And a tree. And a graph. Trees / graphs structures and algorithms are everywhere, too bad Rust language designers forgot that.

Rust's designers are mainly from the C++ world. They know this. And we have seen tons of these datastructures crop up that are written in Rust code everywhere.

The idea is that you find a safe abstraction boundary for you low level data structure, and stick to it. For an LRU cache, the boundary is the LRU cache (not the internal linked list, which needs to be accessed using `unsafe`). The stdlib exposes a linked list that can be used standalone, but as part of a larger unsafe datastructure you should copy it out and integrate directly to get finer grained unsafe access.


Sure, they wanted a safe language. What they actually did, they created a language where data is a second-class citizen.

The strategy “find a safe abstraction boundary for you low level data structure, and stick to it” works well for a scripting language. Or for high-level code that’s not performance-critical, like business logic.

It doesn’t work well for a general-purpose language that’s used for algorithms-heavy performance critical tasks.

There’s no “low level data structures”, because data structures are all the way down, and composed all the way down. Very often I need unsafe things at my API boundaries, because performance or because interop. I can’t implement my data structures once and stick to them, because as I work on the project I change both code and data structures (the main input here is profiler).

That safety thing makes data structures impractical. As you see from my linked list example, they are hard to create, use, and compose.

BTW, graphs (including trees and lists) are just one example how pointers are useful in data structures. Here’s another one.

In C++, I can add an index to a collection using unordered_map<tKey, Value*>. This index allows constant-time lookups, and if the tKey is something small like an int, the RAM cost is very low, because only keys + pointers are stored in the hash map, not the values themselves.

If I’ll want to do same in Rust, I suppose you’ll tell me I need to copy-paste the collection source code and add index there, because what’s in stdlib ain’t composable the way I want :-)




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: