The Rust Programming Language book states in Chapter 4, "Ownership is Rust's most unique feature," etc...
Under the chapter subheading "Ownership Rules" it further states "Each value in Rust has a variable that's called its owner."
This is obviously an important statement with consequences throughout the language. So why is there no definition of "value" anywhere in the Rust documentation or index?
It seems improper to make assumptions about what "value" means or to simply use it colloquially even if it is a generic programming term.
Rust does not yet have a fully elaborated memory model, which is what would be needed to give a really precise meaning to "value" in the sentence you quoted. Work is ongoing on a model called "Stacked Borrows" (paper -- note, I have not read this myself) that is expected to assume this role eventually.
A reasonably accurate informal interpretation is that "value" means "accessible piece of memory".
The Place Expressions and Value Expressions section in The Rust Reference give a definition for what types of expression can be used with a value, and that Expressions chapter explains more about how values can be used overall. It's probably not the precise definition you were looking for, but would that help answer your question?
I'm not sure The Rust Programming Language would be the right place to put a technical definition of value. C++ have a well-defined memory model and the concept of lvalues and rvalues and rvalue references leads to considerable confusion. Instead it's much more accessible to leave "value" as some abstract thing you can perform operations on and let people build up an intuition.
I find this question somewhat disturbing philosophically. What does "value" ever mean, anywhere?
I could argue there are no values anywhere in computing. What we have is a machine with bits in memory (where bits and memory are states of actual hardware things). What "value" or meaning they may have is entirely up to us humans to conjure up in our own minds.
Turning to a mathematical definition of "value" I find from wikipedia:
The value of a variable or a constant is any number or other mathematical object assigned to it.
The value of a mathematical expression is the result of the computation described by this expression when the variables and constants in it are assigned values.
The value of a function, given the value(s) assigned to its argument(s), is the quantity assumed by the function for these argument values.[1][2]
Ok, fine. So there we have it, a cyclic definition, mathematics works with values, but values are defined by how mathematics works.
As such why worry about the definition of "value" in Rust or any other programming language? The language works with values, values are defined by the language.
Somehow I reminded of "Zen and the Art of Motorcycle Maintenance" here.
Edit: By the way, as far as I recall "value" has always been a generic programming term. Variables have values. In my world that has been natural since learning BASIC in 1974.
I usually use the name "value" to mean a sequence of bits, but, if such bits contain some references, such references must be replaced by their destination. According to this meaning, you should consider two values of the same type to be the same value is their equality operator returns "true". Two equal values are actually just one value. This is the mathematical definition of "value".
On the other side, for a memory area referred to by a variable or by a reference, I use the name "object". Two objects are the same object if they have the same memory position at the same time. Any initialized object contains a value. Two distinct objects may contain the same value or different values.
Though, other people use the word "value" also for what I use the name "object".
Can somebody explain to me what this "memory model" thing is that I have been hearing about in recent years, in the C/C++ world and now Rust?
I have been programming since 1974 and never heard of such a thing until recently, never knew I needed a one.
The link given says nothing about the "memory model" idea so I'm none the wiser.
Apparently "memory model" is a big unsolved problem today. My "memory model" is simply that place where the variables in my programs keep their values. Typically an array of hardware elements, be they relays, transistors, ferrite cores, whatever, typically used in a binary fashion to hold values.
Zero sized types throw a wrench in any definition that mentions bits or memory. In this sense, a value is a more abstract, "thing".
At the same time, the zero sized types get optimized away so this distinction doesn't really matter...
So I guess it depends on whether you want the definition of value to be based on the theoretical semantics of the program vs the actual execution of the program.
Of course, for any given type, there exist only one zero-sized value, the empty sequence of bits, like there is only one empty string value, as any empty string is equal to any other empty string.
Iâve personally gotten a better intuition for why programming languages like Rust need a âmemory modelâ thatâs more than just the usual âwhatever the hardware doesâ, or itâs friend âmemory is a big array of bytes and pointers are integer indices into that arrayâ by reading some posts on @RalfJungâs blog. Looking back at the blog, Iâm finding three posts that directly address the topic of memory, in particular all somewhat related to âuninitialized memoryâ:
In case you havenât yet, Iâd recommend those for a good read.
Of course his blog also talks a lot about âstacked borrowsâ which is probably even more on topic when it comes to memory models but I guess reading through that is more of a rabbit hole and also slightly less clearly motivated. And âstacked borrowsâ definitely donât apply to C++ and other languages anymore (whilst pointers and uninitialized memory can be (and are) discussed in a more language-independent context).
I get the impression from at least the C/C++ side of things that a âmemory modelâ was originally necessary in order to define correct behaviour in multi-threaded programs, particularly when there are multiple cores and multiple caches to be synchronized, with different CPU/memory architectures making subtly different guarantees at the lowest level, and compiler optimizations potentially reordering or omitting memory accesses. In that sense itâs probably something thatâs only become a mainstream term in recent years. Other things (like initialization, r/l/x-values in C/C++, maybe ownership/borrowing in Rust) that are relevant to even single-threaded memory-correctness seem to have since fallen under the memory model termâs umbrella too.
A memory model is formal rules and definitions that define how things interact in a programming language, in particular how operations manipulate memory and the whole ownership/borrowing system.
It's just like how you and I don't need to understand how Relativity works in order to use a GPS, but at some point someone needed to figure out the maths. Once they had a solid foundation, other people could build things on top of it knowing their assumptions were logically sound.
I don't find that explanation particularly satisfying.
Certainly I don't understand relativity beyond a bit of Special Relativity and I would not expect most people to grasp that or, as you say, even need to.
However, once you can demonstrate or otherwise convince people of the finite (and fixed) maximum speed of light then it's likely they start to realize there is a problem there, that their simple notions of space, time, speed, etc may not be sufficient in some situations. Even if they don't have the where with all to think about it any further.
Oddly enough I recall this happening to me when I was about 9 years old, when my father happened to mention that nothing can travel faster than light.
What then is the analog of the speed of light in physics that compels is to have to have a concept of "memory model" in computing?
After extensive research (wikipedia) I found my answer:
a memory model describes the interactions of threads through memory and their shared use of the data.
It's all about the interaction between compiler optimization, caches, etc and what visibly happens in memory. That is "visible" as in observed by other threads or hardware looking at the memory you are using.
Oddly enough this has strange parallels to the relativity issues caused by the finite speed of light. Different observers can see events happening in different orders or perhaps can't see them happening at all.
I also start to realize why had not heard of this until recently. Seems it is not something that people realized even needed formalizing till recently.
I'd call the memory model of a language how the language expects the programmer's interaction with memory to carry out. What I mean by this is that the compiler can define a set of constraints based on these assumptions, and as long as the programmer is within those constraints it can generate code. An example is threading, however I believe (I don't know if this is included in the proper understanding of a memory model) that how memory access in single threaded environments and memory acquisition occurs are also both parts of defining a memory model. In particular, what is considered "valid memory" and when. For example, "memory which has not been allocated is considered undefined" is an example of such a rule. Another example is to say that the deallocation of any value out of scope is automatic (I believe this is RAII). In the case of GC languages, a rule would be that any object with no remaining references is inaccessible to the user. In the case of threading we're talking more along the lines of "don't read/write to this if another thread is (potentially) writing too".
If we can define these constraints explicitly, not only do programmers now have a general "style" of writing software, but the compiler is now happy to optimize as much as it wants within the given constraints. Additional to that is that it allows us to reason about code which may break these constraints, and any subsequent issues which may arise from doing such.
I suspect most of us have simple model like that in our heads most of the time. A "variable" in our code is a name for some space in memory, somewhere, a sequence of bits as you say. A "value" is a particular pattern of those bits in some format, two's comp, binary coded decimal, IEEEE floats, big/little endian, etc.
Need that be so? I now wonder if the Rust language is abstract enough that variables need not be binary bits at all. What if values were represented by analog voltages stored on single capacitors, or some weird quantum mechanical state of some thing? Could Rust still be used to program such a machine?
Where this simple model falls down is that although we have a "variable" in our program and we see it taking on "values" as we read the code, it need not actually exist in our compiled binary at all! A compiler optimizer could decide the variable is never used and remove it, and all the calculation of it's values, altogether. Or I guess an optimizer could decide that a variable is only used for temporary values during a calculation and decide it does not need any actual space in memory.
What about values that don't actually use any bits like zero-sized types, or values which can't exist but we can still conjure up a pointer to them like an empty enum?
My understanding is that compilers are allowed to perform any optimisations they want at runtime (e.g. by optimising a value out, or only ever keeping it in a register) as long as the programmer would have no way of observing the optimisation.
I'm not sure whether this would come into a language's memory model though.
Compilers work in the world of real computers, whereas a memory model operates on an abstract programming language which only exists inside the human mind. It is then the compiler writer's job to translate this mental model into something which can be executed on silicon.
The memory model problem, as described by wikipedia at least, comes about because there are more entities than just me the programmer observing what my little page of code and it's inputs and outputs do. Those other entities are other threads, or hardware registers, or perhaps even someone sticking a logic analyser on memory chips and observing what my program does. What those entities observing memory see of my code execution is now mediated by the compiler optimizer which has elided and/or reordered operations. It's also mediated by the processor hardware as it also reorders things, speculatively executes things, juggles data around in it's caches etc.
In short, the memory model problem comes about because from top to bottom or compilers and processors lie to us about what they are doing. Hoping that we won't notice their slight of hand and hoping we will be happy that our programs seem to run faster than otherwise.
Given my understanding as described above, I take exactly the opposite view. The whole "memory model" thing comes about because we need to be able to cut through all that abstraction of the programming language and get down to the nuts and bolts of what the real computer (compiler + hardware) is actually doing.
One thing I've learned from reading Ralf's thoughts is that even LLVM's memory model is not as clearly defined as one would hope. The C++ spec gives them a surprising amount of leeway (not always intentionally) which they might take advantage of and not document particularly well.
What I think Ralf &co are working towards is Rust having a much stronger formally defined memory model. Eventually.