Dumb question #1: If a value/variable/object/'other name' is not owned, is it always borrowed?

Or can values (the official Rust name) be referred to as non_owned but not borrowed either?

let s: &str = "s"; <- Is this borrowed?

I prefer to distinguish variable from instance; the former being the name 's' (or an unnamed temporary) and the latter a constructed structure. (Note you can have uninitialized variables.)

I would say there is no such thing as not owned. Just not the owner.

You can have multiple owners in the case of Rc & Arc

The owner can be placed in different categorises; stack, heap, static, thread-local. String literals like you show are static.

Closest to non owned are leaked variables. But even in this case the programmer has taken charge of managing the ownership.

To get not borrowed without forgetful leaking you have pointers Box::into_raw()

Edit to add this comment:
I use structure interchangeably with type.
Categorises was intended to be non exhaustive but does not read so. Sure I was missing something when I wrote it. I think "external" would be a general additional one.

1 Like
  • The variable s owns a value of type &str.
  • The &str owns nothing.
  • The &str borrows a str from somewhere. (But in this case, that place is static, and the lifetime of the borrow can be 'static, so there are no lifetime constraints of the sort we see from borrowing shorter-lived values.)

Compare with if we had written let s2: String = "s".to_owned(); :

  • The variable s2 owns a value of type String.
  • The String owns a value of type str (which it stores in the heap).
  • Nothing is borrowed.
9 Likes

This is more complicated than I thought.
So variable instances own type instances,
which own the actual value?

Ownership is a recursive/transitive concept that applies at many levels. In a simple case like

let x: i32 = 100;

the variable x owns a value of type i32, and that's it. String types are more complicated because string data is variable size (and other things may involve pointers too).

2 Likes

No; "an instance of &str", "a value of type &str", “a &str value”, and “a &str” are all different ways of saying the same thing.

The distinction to notice in my earlier post is that owning an &str is not the same thing as owning a str.

1 Like

In many cases it is useful to distinguish “owning” types in Rust that own memory, and borrowing ones that don’t.

E.g. the difference between HashSet<i32> and &HashSet<i32> is clear, one owns the set, the other only borrows it.

On the other hand, the distinction is not particularly clear-cut, and most everything “owns” something, and be it owning a reference, which itself only borrows something else.

For example, values of primitive types like i32 are typically considered owned of their value, on the other hand, these types own as little heap memory as a reference &T, so they are of a different quality than something like HashSet<i32> or String.

Also, it’s possible to mix data. You can have a tuple (String, &[u8]) (or a struct with such fields), where one component has ownership over some heap data, and the other has a borrow over some other data.

Feel free to skip my side-discussion about T: 'static and T: Clone

Rust does have some clear-cut distinctions on the language level and commonly expressed in APIs.

There is the category of types that don’t borrow anything with a lifetime, expressed with the constraint T: 'static. This is not the most beginner concept in Rust, but it’s a category that would group &'static str together with i32 and String and HashSet<i32> on the more “owning” side of things, and would group &str, or &HashSet<i32> even the hybrids like (String, &[u8]) (each with non-'static lifetime!) on the more “borrowing” side of things.

T: 'static bounds come up in certain types of APIs where (limited lifetime) borrowed data would not be safe to use, like thread::spawn. It’s thus a tool for identifying non-borrowing data cleanly, and mixed cases like (String, &[u8]) are conservatively grouped with the borrowing data.

There is the Copy trait which marks some (but not necessarily all, as it’s opt-in) types that don’t own (or mutably borrow) any heap data (or other managed resources, like files, etc…) at all. Since it’s opt-in, types can decide to appear more “owning” than they are (or allow becoming more owning in the future) by not implementing Copy. This distinction would group &'static str with non-static lifetime &str together with i32 on the more “borrowing” (i.e. non-owning) side of things, while String, HashSet<i32>, or hybrids like (String, &[u8]) are non-Copy, so more “owning” types under that measure.

The main use-case for the Copy trait do focus on cheap and implicit and known-well-behaved copying of data, though some APIs also use it to guarantee lack of destructors. In any case, it’s thus a tool for identifying non-owning data cleanly, and mixed cases like (String, &[u8]) are conservatively grouped with the owning data.


The T: 'static and T: Clone classifications match my intuitive desription in the first section. The difference between HashSet<i32> and &HashSet<i32> is clear, as HashSet<i32> is 'static but not Copy, and &HashSet<i32> is Copy but (generally) not 'static.

And i32 is both Copy and 'static, whilst (String, &[u8]) is neither Copy nor (generally) 'static.

Mutable borrows also don’t fall under Copy, even though one would typically not consider them owned at all; so yeah, these indicators for characterization are anything but perfect.

Ultimately the concepts “owned” or “borrowed” are just intuitive concepts, and nothing clear-cut, nor anything the compiler directly cares about at all. The compiler cares about some more specific things, like the examples 'static & Clone I mentioned above; and also about lots of more complex things than a simple “owned” vs “borrowed” distinction, like the whole borrow-checker around lifetimes and exclusivity of references.

2 Likes

Yes, but it's a special case, the data is borrowed from the binary file your program compiled to. This means the data is available as long as your program runs, every time it runs.

Ah, then I was getting variable and instance mixed up again.

So variables own type instances
and it's data types are the owners of the actual assigned value?

This terminology is unconventional so it's a bit hard to parse and evaluate. In particular I am not sure what you mean by "instance". I would describe it like this instead.

Values are always owned by something. Variables own values. Some values (like literal strs like "s") are not owned by any variable you can name, so you can consider them as being owned by the program. A type can neither own a value nor be owned - types are simply properties of values and variables.

No, variables own instances/values and those instances/values may own other instances/values.

A simple example where all the machinery is visible in terms of language elements:

struct Foo {
    bar: Bar
}

struct Bar {}

let x = Foo {
    bar: Bar {}
}

The variable x owns a value of type Foo which owns a value of type Bar.

3 Likes

Values owning values?
I think the terminology needs to be in order to make sense of this, mine included.
Values owning values sounds as though '5' can own '3'.
How about using the term object again?

Like an object can have a value and can be a value.
Then you have primitive objects containing primitive values
and when one speaks of values owning values,
one means primitive values owning primitive values
and one should speak of objects owning objects instead?

So variable x owns an object of type Foo which owns an object of type Bar.
So then in here..

let y: i32 = 100;

variable y owns an object of type i32 with value 100.

The use of "instance" came about after the ambiguous use of object with OOP.

Problem is we say two variables have the same value. (Then sometimes contrive a relationship between them even though none may exist. Worse yet write DRY code for this none existent relationship. Some poor sod may have to read it then.)

Definitely don't write those merged.

Looks to be missing apostrophe as abbreviation of instance of type. ( :face_vomiting: no I'm being a grammer natzi.)

Values owning values sounds as though '5' can own '3'.

Integers don't own anything else. This is a fact about the integer types, not about ownership.

How about using the term object again?

Sometimes the term “object” gets used, but in Rust (unlike some other languages) there is no special category of “objects” — especially not distinguished with “primitives” or “primitive values”. In particular, in Rust there is no strong distinction (that is relevant to understanding ownership or designing programs) between types like i32 and types like, say, MyWrappedInt where

struct MyWrappedInt {
    contents: i32
}

An i32 doesn't have any smaller parts that you can name (except for the four bytes that make it up) but that does not mean that it plays a different role in ownership.

How did the term object become too ambigious?
I would say value is too ambigious due to it being expressed in numbers colloquially.
"My car has a value of 5 Bitcoins."
"The queen has a value of 9 points in chess."

ISO C++, which predates most OOP languages in current use, uses the term "object" for almost anything which occupies memory (excludes functions, but includes function pointers). The ISO spec doesn't need a special word to distinguish struct instances from other data.

I think instance and object don't get used by official rust material because they wanted to clearly differentiate themselfes from OOP languages. By doing this they hoped to not imply things like inheritance which rust proudes itself to not have.

Some quotes from the book

"Even though structs and enums with methods aren’t called objects, they provide the same functionality, according to the Gang of Four’s definition of objects."
Characteristics of Object-Oriented Languages - The Rust Programming Language.

"If a language must have inheritance to be an object-oriented language, then Rust is not one. There is no way to define a struct that inherits the parent struct’s fields and method implementations without using a macro."
Characteristics of Object-Oriented Languages - The Rust Programming Language.

"We’ve mentioned that, in Rust, we refrain from calling structs and enums “objects” to distinguish them from other languages’ objects. In a struct or enum, the data in the struct fields and the behavior in impl blocks are separated, whereas in other languages, the data and behavior combined into one concept is often labeled an object. However, trait objects are more like objects in other languages in the sense that they combine data and behavior. But trait objects differ from traditional objects in that we can’t add data to a trait object. Trait objects aren’t as generally useful as objects in other languages: their specific purpose is to allow abstraction across common behavior."
Using Trait Objects That Allow for Values of Different Types - The Rust Programming Language.

EDIT
Sorry for digressing.

1 Like

Hmmm....

I think from following my own philosophy, I'm coming to the same conclusion.

I've become a follower of my own philosophy,
which is a slight derative of a quite radical (aka crackpot by most)
physics theory (of everything) with about a thousand followers
that starts out with dividing all words
into two fundemental categories as it's core basis,
the first category being the object. The other concept.
I simply added a third one: name.

So according to my offshoot theory, the term object should mean shape + location,
like a table in a room, whereas a concept is a relation between objects.
Objects don't need to be islands and can be sub-objects of other objects, like arms and ceilings.
But then there are also conceptual objects that are defined by not just shape, but also a concept.
A parent (not the computer term) for example is a person and therefore has a shape and location,
but it's tied in relationship to another person, the child or the object would not be a parent.
There is no such thing as a childless parent.
One subcategory of such conceptional objects are data objects like a five dollar bill. They are objects, they have shape and a location, but are conceptional as once a data units (which have shape) on it disappears, it stops being money/a contract/an image.

Therefore I think it should be at least termed like this:

Light blue are thus primary data objects,
which can be categorized into data value objects
and data identifier objects.

Now dark blue and purple are also data objects,
but they are obviously superobjects of primary data objects,
so they should be named something different.

The dark blue one has a name/identifier and a value.
The purple data object has a dark blue objects and thus is a superobject of a dark blue object.

Is 'dark blue' usually called a variable, an instance?
If it's purple is called the same thing, then I think they should have different names to distinguish each other.

Let's bring the abstraction level down a notch. Your diagram depicts something like the following rust code:

let s1: String = "hello".to_string();
let s: &String = &s1;

Clearly, s and s1 are variables, so the purple boxes you've drawn around s and s1 can only be variables. The variables hold data (apparently there is some conflicting usage of the term "value" so I am calling it data for now). Data can be composed of smaller bits of data. The capacity: 5 is a field of a struct. The 3: b'l' is an element of a array. It would not be correct to call them variables. If you are looking for a single word that describes a piece of data that is a part of a larger piece of data, the closest word I can think of is member. I think the word instance is uncommon enough that you will need to define what you mean by it if you want to be easily understood. It's taken like 4 or 5 posts of yours for me to slowly surmise what you mean by instance from context (i.e. an instance of a type, or what is commonly called a value, or what I am calling a piece of data in this post).

1 Like

I have no meaning for instance. I had variable in the title.
Then @jonh said

I prefer to distinguish variable from instance; the former being the name 's' (or an unnamed temporary) and the latter a constructed structure. (Note you can have uninitialized variables.)

And then I said to myself: "I guess I should put instance in the title then."

Hmmm.... a member object implies a relationship to larger object.
Perhaps I think it would be better to so far just stick with
DAta Identifier Objects (red surrounded by light blue) and
DAta Value Objects (green surrounded by light blue) for this question.
Or daios and davos.
And then I think what @jonh means by instance
and what @kpreid means by value is a davo.
And what @jonh was talking about was that he considers to be a variable
is not the purple part, but just the daio of the variable.

And if I understand correctly a davo is always owned by a daio.