Apple chips share the same physical memory between the GPU and the CPU. Still, they don't have USM/UVM (Unified Shared Memory/Unified Virtual Memory), that is, the GPU and the CPU can't access the same data concurrently and easily. Programs must map/unmap pages to control which device accesses it, and that's a very expensive operation.
It's not just doubly linked lists. They're just a basic example of borrow checking not being able to handle a lot of easy patterns that are perfectly safe in other languages.
The easy pattern of graph structures is handled by the easy implementation of adjacency lists, which is how you should have been implementing it in other languages too. The specific incompatibility is with the naïve hand-implementations of these structures; the specific thing Rust does not optimize for is toy code.
You know nothing of what I'm programming. You don't know when, whether, or how my program accesses or deletes data. You don't know what domain I'm in, whether I'm on a team, or what our priorities must be.
And yet, you proclaim that your approach is how I should always do it in any language, in any situation.
It strikes me as particularly convenient, seeing as that's the only approach that the borrow checker can reason about.
To make it more concrete: if we apply your solution everywhere we run into this problem with the borrow checker, it can flatten a lot of our program's (non-temporary) state into basically relational tables. Sometimes that's okay, but for a lot of situations it can be bad for encapsulation, velocity, and modularity.
I agree with the sibling commenter that the parent poster was likely using the impersonal you. It is true that we don't know anything about you. You haven't volunteered any specifics, so we're kind of forced to guess if we want to try and respond in a productive manner.
To that end, I think that they chose a very good example. Abstractly, doubly linked lists are a special case of undirected graphs. So pointing out that a naive graph implementation is generally sup-optimal, and that usually one can more effectively model them using different data structures that Rust (and functional languages, and databases, and files, etc.) can handle just fine, neatly addresses problems that one might solve using a doubly linked list, as well as a whole host of other cases, in a single stroke.
Fair, but, if so, could we focus on one of those instead?
I'd be especially interested to see an example that's perfectly safe in C or C++, and for which there's no better way of doing the data modeling in Rust. Using the kinds of data patterns that have long been used to avoid circular references in functional programming languages, for example. Because we want to avoid getting too caught up in examples of things that should be hard in Rust because Rust is designed to favor different ways of doing things.
Or, to put it cutely, I'd want to be cautious about doing the equivalent of criticizing a Toyota for having a terrible glide slope.
Perfectly safe at the expense of a garbage collector. You can still do pointer soup and int handle based lists as you can do in any other low-level language with the same caveats. Rust is punching up from the manual memory management, zero-cost abstraction plane and competing with the garbage collected langs. It's still at it's core a tool for writing low level code.
GP was confused about why the doubly linked list case was so important, so I enlightened, it's an example of an entire class of the borrow checker's problems.
Your response isn't really relevant in that context, and instead escalates to a much larger debate, so best I not engage. Appreciate the thought though =)
These patterns are not "easy" at all - for one thing, they're non-compositional and anti-modular. They're exactly the kind of thing `unsafe {}` was intended for - though it would be nice if Rust came with a way of writing proofs of safety for these things, and this is a field of active research.
I've experienced this as well. Any disagreement always comes down to "you just haven't used it enough". I let myself take that gaslighting for years before I decided to just not go to the Rust servers any more.
And then when you question any of the classic Rust axioms (such as safety or speed being only desirable rather than an absolute commandment) a lot of them tend to get rather hostile. It's not a pleasant crowd if you don't think 100% like they do.
Sorry to hear that. I certainly don't think Rust is ideal for every use case. And it is definitely hard to learn. I think people get a bit too zealous promoting it, simply because it has solved a lot of painful problems that many of us have battled with for decades in C++/C land.
Single ownership often introduces bugs of its own. In Java, I never need to create an Err if the thing my ID is "pointing" to isn't around anymore in whatever hash map the borrow checker made me store it in.
> In Java, I never need to create an Err if the thing my ID is "pointing" to isn't around anymore in whatever hash map the borrow checker made me store it in.
That absolute claim is simply not true. That's like saying that people need to avoid shared mutability in every program, which simply doesn't make sense for a lot of programs, especially if you have a garbage collector already.
It's only a useful tool that has some nice qualities in some settings. It's not some underlying truth of the cosmos, it's just a situationally useful way to do things.
Easier than C or C++ for sure. But I definitely wouldn't say that Rust as easy as a GC'd language like Java or Go.
They have strictly less constraints to deal with, they can let you use the simplest pattern for the situation, and (in my experience) they have less superfluous refactoring sessions due to the overly conservative nature of the borrow checker. I like how Matt Welsh put it in https://mdwdotla.medium.com/using-rust-at-a-startup-a-cautio...:
> What really bites is when you need to change the type signature of a load-bearing interface and find yourself spending hours changing every place where the type is used only to see if your initial stab at something is feasible. And then redoing all of that work when you realize you need to change it again.
Perhaps you're instead speaking of a specific situation or kind of program where the borrow checker happens to be no harder than any other language?
It's fine to give a different viewpoint, but it's surprisingly common that whenever someone says anything bad about Rust, a Rust user comes in to say that they just haven't used it enough.
It's like saying "If you don't like my favorite movie, you just haven't watched it enough."
It's not even the right category of criticism. The author talks about 'mathematical purity' which is something Rust sneers at. Spend just a little while with Rust and you can see that when the language is getting in your way, the driving direction it is pushing you in is towards code that will be maintainable. Which is still not about getting shit done, and is still a valid axis to prefer Go's rapid development cycle on, but if you cannot name the distinction then it is hard to believe you have spent enough time with the language to properly observe it.
I kinda agree, it's weird to me how people make a big deal about the borrow checker as if it's an evil spirit gatekeeping your code.
And if you wanna go fast instead of writing maintainable code you surely can with stuff like unwrap(), clone(), Copy.
In a way I think most people expect to pick up Rust more easily because they have expertise in other languages and are surprised when Rust feels like learning to code again, basically turning some ingrained assumptions upside down.
It really is not the kind of language where you can glaze over the introduction chapter and churn a TODO app in the same afternoon in my opinion.
>And if you wanna go fast instead of writing maintainable code you surely can with stuff like unwrap(), clone(), Copy.
That's a good approach to learning the language, but it breaks down as soon as async comes into the picture. It's basically impossible to be fluent writing async rust without a very healthy understanding of complex types, how the borrow checker works passing things across closures, etc.
I'm sometimes a bit confused when people say that Rust has less cognitive load. I assume that's compared to something like C or C++, right?
I've used Rust and (imperative-style) Scala a lot, and I have to say, Scala is way easier since it doesn't have to deal with the borrow checker, but it still has an amazing type system.
Nim is statically typed and has algebraic data types, so I imagine it's also as easy as Scala, easier than Rust.
I like Rust well enough, but thinking about memory management is a reasonably large effort that I generally don't have to make in most languages. Rust, in not having a GC, strictly adds to that load compared to most languages out there. The exceptions are languages like C, C++, and assembler, and there I agree that Rust seems to make things a fair bit easier.
(Though, disclaimer, I never got around to learning modern C++, which I gather is a huge improvement over the style of C++ I do know.)
I'd be willing to accept, though, that Rust's lack of full-blown OO features, and its strict controls on mutation, often makes things easier in large shared codebases. Perhaps not letting people do some particularly wild things that I'm used to seeing in Java and C# at my day job. I'm not in a position to have a worthwhile opinion on that claim, though.
Imagine if someone said "I've used Python and Scala and Python is way easier since I don't have to deal with a type system". That's where people are coming from.
The borrow checker isn't a hindrance, it's an aid. Rust gives me syntax to describe lifetimes and a tool to check that syntax.
Yes, but if you have immutability and a garbage collector (and can afford to use some extra memory overhead), the borrow checker and lifetimes aren't really all that necessary to bother with.
Rust will never be used as widely as GC'd languages like Java, Go, C#, etc.
It's incredibly well-designed, but you can't design away the inherent complexity of writing safe, secure, fast code. There's no way to make that easy. It will always take a lot of passion or training to really master it. (Yes, I know that some of us here "had an easy time" and it "only took a few weeks", I'm referring to the average person.)
And you know what, that's okay. There are lots of industries where only the most hardcore of programmers use the professional-grade tools. There's nothing wrong with that.
Everyone else will probably continue to use tools like Java, Python, etc. because for most situations, they can get the job done with less training, which means less spent on developer salaries, which is most often more than server costs anyway.
I think the best we can hope for is that Rust is taught in university, like how other industries teach usage of their own professional-grade tools. But even that won't mean that most programmers use Rust, I think.