Borrowing

How to borrow values with references without transferring ownership.

Intermediate⏱️ 40 min📚 Prerequisites: 1

Borrowing

Borrowing allows us to use values without transferring ownership. We use references (&).

References

RUST
let s1 = String::from("hello");
let s2 = &s1;  // s2 is a reference to s1
// s1 is still the owner

Immutable References

RUST
fn calculate_length(s: &String) -> usize {
    s.len()  // we use s, but don't own it
}

let s = String::from("hello");
let len = calculate_length(&s);
println!("{}", s);  // s is still usable!

Mutable References

RUST
fn change(s: &mut String) {
    s.push_str(", world");
}

let mut s = String::from("hello");
change(&mut s);

Borrowing Rules

  1. At any time, you can have either one mutable reference OR any number of immutable references
  2. References must always be valid
RUST
let mut s = String::from("hello");
let r1 = &s;      // OK
let r2 = &s;      // OK - multiple immutable references
// let r3 = &mut s;  // ERROR!

Code Examples

Immutable Reference

Borrowing a value with an immutable reference

RUST
fn main() {
    let s = String::from("hello");
    let len = calculate_length(&s);
    println!("The length of '{}' is {} characters.", s, len);
}
fn calculate_length(s: &String) -> usize {
    s.len()
}

Explanation:

The `&s` is a reference to the String. The function can use the value but doesn't own it. The s variable remains usable after the function call.

Mutable Reference

Modifying a value with a mutable reference

RUST
fn main() {
    let mut s = String::from("hello");
    change(&mut s);
    println!("{}", s);
}
fn change(some_string: &mut String) {
    some_string.push_str(", world");
}

Explanation:

The `&mut s` is a mutable reference that allows modification of the String. The variable must also be `mut`.

Borrowing Rules

Demonstrating borrowing rules

RUST
fn main() {
    let mut s = String::from("hello");
    let r1 = &s;
    let r2 = &s;
    println!("{}", r1);
    println!("{}", r2);
    let r3 = &mut s;
    println!("{}", r3);
}

Explanation:

At any time, you can have either multiple immutable references OR one mutable reference. The compiler checks these rules.

Blockchain: Validating Without Ownership

Using borrowing for blockchain validation

RUST
struct Transaction {
    from: String,
    to: String,
    amount: u64,
    fee: u64,
}
fn validate_transaction(tx: &Transaction, sender_balance: u64) -> bool {
    tx.amount > 0 &&
    tx.fee >= 10 &&
    sender_balance >= tx.amount + tx.fee
}
fn main() {
    let tx = Transaction {
        from: String::from("0xAlice"),
        to: String::from("0xBob"),
        amount: 100,
        fee: 10,
    };
    let balance = 1000;
    if validate_transaction(&tx, balance) {
        println!("Transaction is valid");
    }
    println!("Transaction: {} -> {} ({} tokens)", tx.from, tx.to, tx.amount);
}

Explanation:

Borrowing is essential in blockchain for validation. We can check transactions without taking ownership, allowing the transaction to be used elsewhere (e.g., added to a block).

Blockchain: Updating State with Mutable Borrow

Using mutable references to update blockchain state

RUST
use std::collections::HashMap;
struct BlockchainState {
    balances: HashMap<String, u64>,
}
fn transfer_funds(
    state: &mut BlockchainState,
    from: &str,
    to: &str,
    amount: u64
) -> Result<(), String> {
    let from_balance = state.balances.get(from).copied().unwrap_or(0);
    if from_balance < amount {
        return Err(String::from("Insufficient balance"));
    }
    *state.balances.entry(from.to_string()).or_insert(0) -= amount;
    *state.balances.entry(to.to_string()).or_insert(0) += amount;
    Ok(())
}
fn main() {
    let mut state = BlockchainState {
        balances: HashMap::new(),
    };
    state.balances.insert(String::from("0xAlice"), 1000);
    state.balances.insert(String::from("0xBob"), 500);
    transfer_funds(&mut state, "0xAlice", "0xBob", 200).unwrap();
    println!("Alice: {}", state.balances.get("0xAlice").unwrap());
    println!("Bob: {}", state.balances.get("0xBob").unwrap());
}

Explanation:

Mutable references allow updating blockchain state (like balances) without taking ownership. This is crucial for state management in blockchain nodes.

Exercises

Using References

Write a function that returns a String's length using a reference!

Medium

Starter Code:

RUST
fn main() {
    let s = String::from("Rust");
    let len = get_length();
    println!("The length of '{}' is: {}", s, len);
}