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.

Leave a Reply