Ownership & Borrowing

A visual guide to ownership, borrowing, moves, lifetimes and memory safety for programmers coming from C.

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

Ownership is primarily about responsibility for an object's lifetime.

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.

name owner
owns
String object "Alice"
represented by
Storage physical representation
Important:

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:

w C pointer
contains address
Widget heap object

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 pointer
?
Widget one object
b pointer
?

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 C problem:

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:

"Which memory addresses does name own?"

Instead ask:

"Which object does name control the lifetime of?"
name owner
owns
String "Alice"

The owner is the value/storage location whose responsibility includes the object's lifetime and eventual destruction.

Think: Owner = responsible for lifetime.

4. Is It the Memory Slot?

This is probably the most important question for someone coming from C.

No. Do not think of ownership as permanently attached to a particular memory slot.

It is tempting to imagine:

variable X memory slot
owns
memory #1234 bytes

But that is too implementation-focused.

A better model is:

variable X owner
owns
String object the thing that exists
represented by
Storage implementation detail
Rule of thumb:

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:

a owner
owns
String "hello"

After the move:

a no longer owner
ownership moved
b new owner
owns
String "hello"
Move = transfer of responsibility.

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.

Before
a owns object
move
After
b owns object
Important:

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:

name owner
controls lifetime
String "Alice"
p borrower
access only
Borrowing means temporary access without ownership.

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.

7. Shared Borrows

A shared borrow means:

"I want to read this object, but I don't need exclusive access."

Multiple shared borrows can exist simultaneously.

p shared borrow
read
String one object
q shared borrow
read

The owner still exists behind the scenes:

Shared rule:

Multiple readers are okay. They do not require exclusive mutation.

String name = make_string("Alice");

String* p = &name;
String* q = &name;

/* p and q can both read name */

This is conceptually very different from unrestricted pointer aliasing in C.

8. Mutable Borrows

Now suppose we want to modify the object.

mut String* p = &mut name;

The important idea is:

A mutable borrow provides exclusive mutable access.
p mutable borrow
exclusive access
String being modified

Why exclusive?

Imagine two pointers:

p writing
Object being modified
q reading
Potential conflict:

One accessor is changing the object while another accessor is relying on its state.

Simple rule:

Many shared readers are okay.

One mutable accessor gets exclusive access.

9. Lifetimes

Borrowing immediately creates another question:

"How long is this borrowed access valid?"

Suppose:

String name = make_string("Alice");

String* p = &name;

The borrow cannot remain valid after name is destroyed.

Object lifetime
Owner lifetime
Borrow lifetime
Core lifetime rule:

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:

"Who is responsible for destroying the object?"

The owner.

name owner
controls lifetime
String resource
eventually
destroy release resources
Ownership connects three questions:
  1. Who is responsible for the object's lifetime?
  2. Who is allowed to access it?
  3. 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.
Think of raw pointers as an escape hatch.

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 major improvement is not simply "different pointers."

The language can distinguish between owning a value and temporarily accessing somebody else's value.

13. The Three Most Important Relationships

OWN

Owner responsible
owns
Object has lifetime

The owner controls the object's lifetime.

BORROW

Borrower access
accesses
Object owned elsewhere

The borrower gets access without becoming responsible for destruction.

MOVE

Old owner before
transfers ownership
New owner after

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?

OWNER responsibility
owns
OBJECT has lifetime
may be accessed by
BORROWER temporary access
Ownership answers "Who is responsible?"

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?

No.

The important semantic change is the transfer of ownership responsibility.

Question 3: Can several shared borrows exist simultaneously?

Yes.

Shared borrows permit compatible read access.

Question 4: Why is mutable access normally exclusive?

Because mutation can conflict with other accesses.

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

"I am responsible for this object's lifetime."

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.
The four questions to ask when reading ownership code:
  1. Who owns this object?
  2. Who is allowed to access it?
  3. Is that access shared or exclusive?
  4. 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

Don't start by asking "where are the bytes?"

Start by asking:

"Who is responsible for this object's lifetime, and who merely has permission to access it?"