1. The Mental Model
If you come from C, the most useful first step is to stop thinking about ownership as "owning a memory address."
A variable that owns an object is responsible for the object's lifetime and eventual destruction. A borrow gives another part of the program temporary access to that object without transferring that responsibility.
There are three concepts that are easy to accidentally mix together:
Object
The thing your program considers to exist. For example, a String, a Widget, a Vector, or a user-defined structure.
Storage
The physical representation of the object: stack memory, heap memory, registers, or whatever representation the compiler chooses.
Ownership
A language-level relationship describing who controls the object's lifetime and destruction.
Ownership is not fundamentally a property of a particular collection of RAM addresses.
It is a semantic relationship involving an object, its lifetime, and the storage/value responsible for it.
2. Start With What You Know: C
Let's begin with a familiar C example.
Widget *w = malloc(sizeof(Widget));
At a low level, you can imagine:
But C doesn't tell the compiler what the pointer means.
Is w the owner?
Is it borrowing the object?
Can another pointer free it?
Is somebody else responsible?
Widget *a = malloc(sizeof(Widget)); Widget *b = a;
A human programmer might establish a convention such as:
a owns the Widget. b merely borrows it.
But the ordinary C pointer type does not encode that distinction.
The compiler generally cannot tell whether a pointer owns an object, merely borrows it, or is dangling.
Double free
free(a); free(b);
Use after free
free(a);
printf(
"%d",
b->value
);
Memory leak
Widget *a = malloc(...);
/* forgot free */
3. What Does "Owned" Actually Mean?
Suppose we have:
String name = make_string("Alice");
The useful question is not:
name own?"
Instead ask:
The owner is the value/storage location whose responsibility includes the object's lifetime and eventual destruction.
4. Is It the Memory Slot?
This is probably the most important question for someone coming from C.
It is tempting to imagine:
But that is too implementation-focused.
A better model is:
If thinking in terms of addresses makes ownership confusing, temporarily forget addresses.
Think in terms of: object + lifetime + access.
5. Moving Ownership
One of the biggest differences from C is the idea of explicitly transferring ownership.
String a = make_string("hello");
String b = move(a);
Before the move:
After the move:
The important semantic event is not necessarily that bytes moved somewhere else.
The important event is that ownership responsibility
transferred from a to b.
Why is this useful?
Because destruction remains unambiguous.
Do not automatically equate "move" with "copy the bytes somewhere else."
Think: the ownership relationship moved.
6. Borrowing
What if we don't want to transfer ownership? We simply want another function to use the object.
String name = make_string("Alice");
String* p = &name;
Conceptually:
The borrower gets permission to access the object while somebody else remains responsible for its lifetime.
Compare this with C
In C, you might simply pass a pointer:
void print_widget(Widget *w) { printf("%d", w->value); }
A programmer may intend:
"print_widget borrows w."
But the C type Widget * doesn't encode that
ownership relationship.
In an ownership-oriented language, the managed pointer represents an access capability whose lifetime and aliasing are subject to the language's rules.
8. Mutable Borrows
Now suppose we want to modify the object.
mut String* p = &mut name;
The important idea is:
Why exclusive?
Imagine two pointers:
One accessor is changing the object while another accessor is relying on its state.
Many shared readers are okay.
One mutable accessor gets exclusive access.
9. Lifetimes
Borrowing immediately creates another question:
Suppose:
String name = make_string("Alice");
String* p = &name;
The borrow cannot remain valid after name is
destroyed.
A borrow must not remain usable after the object it refers to has ceased to exist.
Compare this with C
Widget *get_widget(void)
{
Widget w;
return &w;
}
The pointer returned by this function refers to a local object whose lifetime ends when the function returns.
C allows you to form the pointer. Using it later is undefined behavior.
An ownership-aware language attempts to detect this kind of invalid lifetime relationship.
10. Destruction
Ownership becomes particularly useful when we talk about destruction.
fn example()
{
String name = make_string("Alice");
// use name
} // ownership ends here
The important question is:
The owner.
- Who is responsible for the object's lifetime?
- Who is allowed to access it?
- Who eventually destroys/releases it?
11. Managed Pointers vs Raw Pointers
This is especially important when coming from C.
| Concept | Ownership-oriented language | C |
|---|---|---|
| Managed pointer | Represents a compiler-checked access capability. | No direct equivalent in ordinary C. |
| Raw pointer | An unmanaged address. Safety guarantees do not automatically apply. | The normal pointer model. |
| Ownership | A language-level concept. | Mostly a programmer convention. |
| Lifetime checking | Enforced by the language's safety rules. | Mostly programmer responsibility. |
They bring you closer to the traditional C world: "Here is an address. You are responsible for knowing what it means and whether it is valid."
12. The Same Problem: C vs Ownership
C version
Widget *create_widget(void)
{
Widget *w = malloc(sizeof(Widget));
if (!w)
return NULL;
w->value = 42;
return w;
}
void use_widget(Widget *w)
{
printf("%d\n", w->value);
}
int main(void)
{
Widget *w = create_widget();
use_widget(w);
free(w);
}
A human reader may infer:
create_widget
Creates an object and transfers responsibility to the caller.
use_widget
Uses the object but does not destroy it. Conceptually, it borrows it.
main
Holds the responsibility and eventually calls
free.
But those relationships are mostly in the programmer's head.
Ownership-oriented version
Widget w = create_widget();
use_widget(&w);
/* w remains owned here */
/* scope ends - w is destroyed */
The language can distinguish between owning a value and temporarily accessing somebody else's value.
13. The Three Most Important Relationships
OWN
The owner controls the object's lifetime.
BORROW
The borrower gets access without becoming responsible for destruction.
MOVE
Responsibility for the object's lifetime transfers.
14. Common Mental Mistakes
Wrong mental model
"Ownership means owning a memory address."
Better mental model
Ownership means responsibility for an object's lifetime.
Wrong mental model
"A pointer means ownership."
Better mental model
A managed pointer can represent non-owning access.
Wrong mental model
"Borrowing means copying the object."
Better mental model
Borrowing means accessing the existing object without taking ownership.
Wrong mental model
"Move means the bytes have to move."
Better mental model
Move means ownership responsibility transfers.
Wrong mental model
"A borrow can live as long as the pointer exists."
Better mental model
A borrow is valid only while the referenced object remains alive and the borrow rules permit access.
15. A Simple Set of Rules
You do not need to memorize the entire specification yet. Start with these rules.
Rule 1
Every owned object has an owner.
Rule 2
Ownership determines responsibility for destruction.
Rule 3
A borrow does not transfer ownership.
Rule 4
A borrow cannot outlive the object it refers to.
Rule 5
Shared borrows permit compatible read access.
Rule 6
Mutable borrows provide exclusive mutable access.
Rule 7
A move transfers ownership.
Rule 8
Raw pointers operate outside normal managed safety guarantees.
16. The Big Picture
Put everything together and ownership languages are really answering four questions.
1. Who owns it?
Who is responsible for the object's lifetime?
2. Who can access it?
Which parts of the program currently have permission to use it?
3. How can they access it?
Is access shared/read-only or exclusive/mutable?
4. For how long?
What are the lifetime boundaries of the object and each borrow?
Borrowing answers "Who may access it?"
Mutability answers "Who may change it?"
Lifetimes answer "For how long?"
Move semantics answer "Who is responsible now?"
17. Translate Ownership Concepts Into C Terms
When reading an ownership-oriented specification, this translation can help.
| Ownership concept | Closest C intuition | Important difference |
|---|---|---|
| Owner |
The pointer/value responsible for calling
free().
|
In an ownership system, this relationship is part of the language semantics rather than merely a programmer convention. |
| Borrow | Passing a pointer to a function without transferring responsibility. | The compiler can enforce lifetime and access rules. |
| Move | Transferring responsibility from one variable to another. | The old value is no longer treated as the owner. |
| Shared borrow | Somewhat like a read-only pointer. | It also participates in lifetime and aliasing rules. |
| Mutable borrow | Somewhat like a writable pointer. | It requires exclusive access during the borrow. |
| Lifetime | The period during which a pointer is known to remain valid. | The language tracks the relationship rather than leaving it entirely to the programmer. |
| Raw pointer | Traditional C pointer. | It is outside the normal managed safety model. |
18. Test Your Mental Model
Question 1:
If a owns a String and b
borrows it, who is responsible for destroying the String?
a.
The borrower does not become responsible for the object's lifetime.
Question 2:
If ownership moves from a to b,
must the object physically move in memory?
The important semantic change is the transfer of ownership responsibility.
Question 3: Can several shared borrows exist simultaneously?
Shared borrows permit compatible read access.
Question 4: Why is mutable access normally exclusive?
Exclusivity lets the compiler reason about changes to the object without uncontrolled aliasing.
Question 5: What is the best replacement for: "I own these memory addresses"?
That is the mental model to carry forward.
19. One-Page Cheat Sheet
| Term | Think of it as... |
|---|---|
| Object | The thing that exists. |
| Storage | Where/how the object is physically represented. |
| Owner | Who is responsible for the object's lifetime. |
| Ownership | Responsibility and authority over lifetime. |
| Borrow | Temporary access without ownership. |
| Shared borrow | Compatible read access; multiple readers can coexist. |
| Mutable borrow | Exclusive mutable access. |
| Move | Transfer ownership to another value. |
| Lifetime | How long an object or borrow remains valid. |
| Destruction | The point where the owner's responsibility ends and resources are released. |
| Raw pointer | An unmanaged address; safety becomes your responsibility. |
- Who owns this object?
- Who is allowed to access it?
- Is that access shared or exclusive?
- How long is that access valid?
20. Where to Go Next
Once this mental model feels comfortable, the natural progression is:
1
Ownership
2
Move semantics
3
Copy semantics
4
Shared borrowing
5
Mutable borrowing
6
Borrow lifetimes
7
Reborrowing
8
Field/partial borrowing
9
Aliasing
10
Borrow invalidation
11
Destruction
Start by asking:
"Who is responsible for this object's lifetime, and who merely has permission to access it?"