I think part of what makes programming languages hard to learn is that two authors rarely can agree on the same terminology. Reading a mixture of sources can lead to confusion about what's going on.
This article explains the uniqueness/mutability relation well, but it uses the terms "variable" and "value" with different meanings than most programmers would use. What the article calls "value" I would call "memory location" (maybe one could even call it "variable"); and what it calls "variable" isn't 100% accurate but I guess it works to keep the explanation simple.
memory location is maybe not a great replacement for value in this article. values are preserved when moving them, but the memory location changes. The location doesn't matter for the purpose discussed in the blog post.
Models where the value `1` in `let x = 1` gets a memory location - they unnecessarily mix in details about where the value is stored. In most cases, the value is not stored anywhere, neither stack nor heap etc, that is just a value and the compiler and compiler settings determine what happens next. A common case is that it's constant folded into some operation, so the `1` then doesn't have any relationship to memory at all.
I think the terms used are typical for Rust. Doesn't each language have its own terms? The C standard's object is not the same as Python's object and so on.
The compiler calls it "variable" so I wouldn't say it's untypical, "variable binding" seems to be synonymous. I think I've seen "binding" used alone within destructuring/matching patterns, but in the end that fulfills the same purpose.
I don't see what's wrong with variable, it's not like Rust does anything special that warrants a different term.
The usage of the `@` operator is usually called a binding:
match value {
n @ 0..8 => println!("{n}"),
_ => {},
}
It lets you bind and use the specific value within a matched range.
I don't think I ever used it though, I tend to write something like `n if n<8 => {...}` instead
The reason people say binding is because the words “immutable variable” are an oxymoron. It never makes sense to use variable for immutable bindings; they are not able to be varied. Binding is just the meaning of the word “variable” but without the implied mutability.
This aligns with the fact that the mutability is (syntactically and conceptually) a property attached to the binding, and hence there is a need to talk about bindings generally without implying anything about the mutability. Lots of languages don’t have immutable bindings, all bindings are mutable, so you may as well call them variables in C, Go, etc.
"Immutable variable" is not an oxymoron, because the value of a variable can and typically will vary via other means than mutation. The value of a variable is potentially different every time it comes to scope, e.g. whenever a function is called.
I have recently seen "exclusive" for &mut, instead of "unique" here.
The "value" and "variable" naming is unambiguous though. "Variable" might be problematic for someone that conflates the concepts of reference and value - but that's an issue that should be solved far earlier than ownership.
Exclusive is an interesting description in this context because it describes the semantics of the mut as regards the borrow checker. "mut" in Rust means much more than just "mutable".
I agree! "Resource" instead of "value" and "reference" instead of "variable" would bring this in line with the program logic community, from which many of these ideas spring or draw inspiration from.
Reference can't be used for this article since it has a specific meaning in Rust, i.e the most basic safe pointers, those with the & syntax, are references.
UnsafeCell (a wrapper for all the hacky unsafe memory access) is such a great idea. This allows rest of the program to be easier to statically analyze. For everything not in it, the compiler can assume it respects strict aliasing and immutability without any gotchas.
If UnsafeCell didn't exist it, it would be unclear where the programmer really meant to allow data races or poke holes in immutability, etc.
as a language designer.. im still stuck wether ownership is a good or bad idea. I like ownership because that's how we handle things in the real world. Eg. 2 people grabbing potato chips out of a bag at the same time is a bad idea. We pass the bag along. wait turns. But for some reason, we dont do this with computer programs. So.. should we do this with computers or not? Feel free to reply.
My instinct says 'yes'. But i also have a feeling that im going to run into problems that current languages dont have. Such as being forced to copy data or have complex systems like in rust to make the compiler happy.
> But for some reason, we dont do this with computer programs
I'm unsure what you mean here. It's pretty foundational to the structuring of programs hierarchically (eg, trees own their children, functions own their variables, closures own their environments, and so on).
The classic argument against ownership as a language semantic is that it doesn't play well with graphs and circularly linked lists, where there isn't a meaningful hierarchy and therefore no one ancestor is the "owner" of any descendant. The counterargument is that these datastructures are both rare and better represented with a single owner of the data and the relationships represented through indirection even if your implementation language doesn't enforce ownership (for example, adjacency lists/matrices for graphs, vectors for lists, etc).
I just meant that 99% of software was written without the idea of ownership. Sure, we had objects containing other objects, but i dont see that as "ownership".
The term might be a bit new now that we have some work that seeks to establish a formalism for it, but the concepts are very old.
A modern definition could be that ownership semantics define how aliases to data are created and shared. This includes things like variable binding and class members, but also encapsulates pointers and references - which have formal semantics in every language that uses them (and therefore, bake in ownership, even if it is implicit).
In systems programming languages like C and C++ the word "owner" and "ownership" are often used to describe which data structures and calling contexts are responsible for managing data lifetimes, since aliases to that data are used to control memory allocation and deallocation. The semantics are not baked into the language, but most software in these ecosystems has to be written with ownership in mind to be sound - and indeed, has been for many years, with that verbiage ("lifetime", "owner", etc).
I think ownership is necessary to understand as a programmer when designing systems, regardless of whether or not the language has explicit enforcement of it. Having a clear idea of ownership and accommodating it in your data structures is the best way to know when to free an object in C.
Similarly, having a clear idea of ownership can help in writing GC friendly code.
I think some variant of it is a must in low-level languages like Rust, even if as vague as “this given arena owns this and will free these objects”.
For a higher level hobby language I’m designing, I’m thinking of doing a similar concept, but with runtime overhead - basically every object would be “synchronized” in Java’s parlance, but to make it efficient most lockings would be elided at compile time via move semantics. So data races couldn’t happen (also, could possibly do a better job at escape analysis).
The primary reason I’m thinking about that is that we really shouldn’t be using non-thread safe primitives anymore — Clojure did get this right. That should be left as a compiler optimization if it can prove that that given code runs inside a “alone”.
That's distributed ownership. Basically you get ownership of certain objects for no reason. And if you question the Goverment Class about this behaviour, you get freed from memory. Not a good strategy in my opinion.
> It means no private ownership of the means of production, which another can of worms.
Coincidentally, worms are an excellent source of protein in this communist, Marxist society. By eating worms instead of beef, it will also help to stop climate change by eliminating the greenhouse gases from cows, thus saving the planet. You will eat ze bugs!
Does Rust provide support for low level synchronisation related operations such as memory fences? If so is there any way relying on these can break data race freedom (which as I understand Rust guarantees)?
> If so is there any way relying on these can break data race freedom (which as I understand Rust guarantees)?
It’s not clear what your question is.
You can use unsafe to write unsound code which suffers from data races.
If you use safe Rust, then you should not suffer from data races (and fences only add constraints, so they can’t add data races, they’re tools to avoid those), although bugs are always possible and sometimes purportedly safe stdlib APIs are found to be unsound (that’s one of the few things for which the rust project allows breaking backwards compatibility, though even that depends on the blast radius e.g. std::mem::uninitialized is still there, just deprecated, even though there’s almost no way to use it soundly).
Besides other’s answers, while data races are not possible in safe rust, other race conditions can happen (and not even the actor model is immune to these).
This article explains the uniqueness/mutability relation well, but it uses the terms "variable" and "value" with different meanings than most programmers would use. What the article calls "value" I would call "memory location" (maybe one could even call it "variable"); and what it calls "variable" isn't 100% accurate but I guess it works to keep the explanation simple.