No Definition Of "value" In Rust

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.

Are you looking for a definition of value, or just suggesting that one is missing?

1 Like

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".

1 Like

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.

1 Like

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:

Value (mathematics)

In general, a mathematical value may be any definite mathematical object. In elementary mathematics, this is most often a number – for example, a real number such as π or an integer such as 42.

  • 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.

3 Likes

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.

What am I missing here?

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.

1 Like

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.

3 Likes

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).

8 Likes

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.

1 Like

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.

1 Like

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.

5 Likes

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.

No idea yet. Never seen one.

That is my understanding as well.

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.

It's simple. In Rust, everything is a value.

2 Likes

Cantor might say it is not one but rather infinite. :slightly_smiling_face:

1 Like

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.

1 Like