Borrowing (temporary Ownership)

Rust’s Ownership model for data is a major departure from previous programming languages and approaches. This model is so fundamental to Rust, that it influences just about everything – syntax, libraries, etc. To some degree, everything about Rust is downstream of Ownership. For a quick primer on Ownership, see this post.

Ownership excludes the traditional shared access model to data, dramatically increasing data protection. Yet, Ownership imposes very significant restrictions. Developers, and the programs they craft, need the ability to pass data to shared code (e.g., functions) for processing. Ownership, by itself, makes this type of programming impossible, and would relegate Rust to the dustbin.

Borrowing is Rust’s critical companion to Ownership which maintains high data security while allowing code to utilize shared code, libraries, etc. For developers accustomed to passing data by reference, Rust’s syntax and behavior will be very familiar.

struct SomeThing {
    tiny: i8,

fn main() {
    let x = SomeThing { tiny: 74 };

    println!("\n- Get tiny's value by borrowing x");
    // Rust's Borrow operator, &, _is not_ a reference operator (e.g., C++)
    println!("\tvalue returned is {}", get_tiny_by_borrowing(&x));

// This function uses the Borrow operator to require callers to give it temporary ownership
fn get_tiny_by_borrowing(s: &SomeThing) -> i8 {
    return s.tiny;

Rust’s Borrow operator ‘&’ looks the same as C++’s Reference operator. What’s the difference? You guessed it – Ownership!

When we say that function get_tiny_by_borrow borrows SomeThing, we mean that Rust ensures that this function owns the SomeThing instance for the scope of the function. After the function complete, ownership reverts back to the prior owner (x in this case).

Using C++ as a comparison, Rust’s Borrowing differs in two major ways:

Shared vs Owned – C++ data may be referenced by multiple code simultaneously, for example by multiple threads. Data safety is in the hands of the programmer who must ensure the data is protected / thread safe. Rust’s compiler won’t allow this behavior, raising the bar for data / memory safety.

Data changes – What happens when a function changes the data passed to it? Rust and C++ provide the same behavior, if the data is mutable. C++ data is implicitly mutable, so any changes inside the function are carried outside the function. Rust data, however, is implicitly immutable for memory safety, and must be explicitly declared as mutable. In the code sample, SomeThing is borrowed and is not declared as mutable, so attempting to change its data will result in a compiler error.

Mutability is a major topic in itself, so we’ll cover it in a separate post.

Rust’s Big Differences

All developers will find substantial differences with Rust. In one aspect, Ownership, Rust is different that just about every language before it. The following outline gives a brief overview of Rust’s most significant (most difficult?) differences.

  • Variables – immutable by default, structs + methods (no classes), None > null, no implicit casting
  • Ownership – only one pointer at a time
  • Borrowing – sole pointer can be loaned / borrowed
  • Traits – not quite .NET’s interface


Rust’s concept of memory ownership is its most substantial difference from other languages. Almost all languages employ an accessor model for memory. Accessors (aka, pointers, references, etc.) can be copied, passed to other code, etc., which results in multiple accessors to the same bit of memory.

Ownership explicitly replaces an accessor model with the ownership model. Ownership, and its companion Borrowing, underlie Rust’s focus on memory safe code.

For developers who have worked with pointers in languages like C, C++, etc., the easiest way to think about ownership is that only one pointer may point to (own) a memory location. Copying a pointer transfers ownership to the new pointer, and the previous pointer becomes invalid.

More recent OOP languages like Java and C# hide much of the complexity of dealing with pointers directly, but developers still deal with concepts value types (stack) vs object types (heap), passing by value (a copy) vs by reference (a pointer).

Regardless, the ownership model really twists your noodle!

See Ownership for more explanation and examples of ownership.


Ownership is probably the highest hurdle for most developers investigating Rust. Rust is the first language to gain mainstream acceptance that fundamentally changes how memory is used relative to the last five or six decades. Ownership is tectonic shift in how developers think as they write code.

To understand why ownership is so different, let’s consider the historic approach to programs using memory. Since the early days of programming, memory has been a bit like cooks in a kitchen. One cook in the kitchen can do whatever they like and doesn’t have to worry about another cook “corrupting” things. Two cooks in the kitchen can become surprisingly complicated, but they can make it work if they are each very careful – much more careful than when they have the kitchen to themselves. A little carelessness can be immediately disastrous (i.e., grease fire), or can take a bit of time (i.e., water dripping into a plugged sink).

Multiple cooks’ shared access to the kitchen is analogous to multiple accessors to the same bit of memory in a program. Consider this C# code:

   string s1 = "Hello!";
   string s2 = s1;

   Console.WriteLine($"\tString value on heap, s1 = {s1}");
   Console.WriteLine($"\tString value on heap, s2 = {s2}");
   Console.WriteLine($"\ts1 & s2 reference the same object? {object.ReferenceEquals(s1, s2)}");


    String value on heap, s1 = Hello!
    String value on heap, s2 = Hello!
    s1 & s2 reference the same object? True

The two variables s1 & s2 both reference the same memory location which contains “Hello!” Printing the value of either variable produces the same “Hello!”

The problem is that either variable can change the data at the memory location, and the other only cannot do anything to protect the value or itself. (We’re ignoring copy on write behavior in this case)

Continuing the analogy, Rust only allows a single cook in the kitchen. The developer can instruct one cook to take ownership of the kitchen from another. The previous cook no longer has any access to the kitchen at all. Directly translating the C# code to Rust yields:

    let s1 = String::from("Hello!");
    let s2 = s1;

    println!("\tString value (heap) s1 = {}", s1);
    println!("\tString value (heap) s2 = {}", s2);

Attempting to compile this code will fail with

error[E0382]: borrow of moved value: `s1`

This code looks very similar to the C# example. What’s different? Ownership!

On line 1, s1 is set to “Hello!” After line 1, s1 owns the memory location holding “Hello!” But then on line 2, s2 takes ownership from s1. That’s the situation the Rust compiler has detected, and gives an error telling us that s1 no longer has access to the data. Admittedly, the message is a little cryptic, but it will make more sense after we cover Borrowing.

By commenting out line 4, compilation completes successfully because s1 is no longer attempting to access memory owned by s2. Running the code results in:

String value (heap) s2 = Hello!