Borrowing

Well, move semantics sounds like a pain! We really have to explicitly clone things all the time? No, this is where borrowing comes in.

Rather than take ownership of a value, we can borrow it.

fn main() {
    let name = String::from("Tim");

    greet(&name);
    greet(&name);
}

fn greet(name: &String) {
    println!("Hello, {}!", *name);
}

That ampersand in &String means greet doesn't want to take ownership of the String, it just wants a reference to it. And because it's not the owner, the string is not dropped when the function finishes executing. So we can call it a second time with no problems.

Since the name inside greet is not a String, but a string reference, we dereference it when we use it with *name.

This is analogous to the following in Perl.

#!/usr/bin/env perl

use v5.28;
use warnings;
use experimental qw(signatures);

my $name = "Tim";

greet(\$name);

sub greet($ref) {
    say "Hello, ${$ref}!";
}

In fact, Rust will do the dereference for us. That is, this works just fine as well.

fn main() {
    let name = String::from("Tim");

    greet(&name);
    greet(&name);
}

fn greet(name: &String) {
    println!("Hello, {}!", name);
}

This is one of the few times Rust does not make us be explicit.

In Perl, the analogous thing

#!/usr/bin/env perl

use v5.28;
use warnings;
use experimental qw(signatures);

my $name = "Tim";

greet(\$name);

sub greet($name) {
    say "Hello, $name!";
}

would print out something like

Hello, SCALAR(0xDEADBEEF)!

Shared and Unique Borrows

There are actually two kinds of borrows in Rust. The above is a shared (immutable) borrow. We can read the value, but we cannot change it. If we need to do that, we must use a unique (mutable) borrow. (I really like the terms "shared" and "unique", but it seems "immutable" and "mutable" have won out. I guess because of the mut keyword.)

We can have multiple immutable borrows. Lots of things can read a value at the same time. We can only have one mutable borrow. Only one thing at a time can change a value. Moreover, if there is a mutable borrow, there can be no shared borrows. If we're writing a value, then no one should be reading it. Indeed, if there is a mutable borrow, not even the owner can read the value.

fn main() {
    let mut name = String::from("Hank");

    greet(&name);
    change(&mut name);
    greet(&name);
}

fn greet(name: &String) {
    println!("Hello, {}!", name);
}

fn change(name: &mut String) {
    *name = String::from("Dean");
}

In Perl, our references are always mutable. The above might look like this

#!/usr/bin/env perl

use v5.28;
use warnings;
use experimental qw(signatures);

my $name = "Hank";

greet($name);
change(\$name);
greet($name);

sub greet($name) {
    say "Hello, $name!";
}

sub change($ref) {
    $$ref = "Dean";
}

That's not really idiomatic in either language, but you get the idea.

The Rust compiler has a borrow-checker. It keeps track of all the borrows in our programs and notifies us when we've violated any of these rules.

Closures

When we talked about closures, we said they can access variables from the enclosing scope. There are three ways to do this: move and the two kinds of borrows. There are traits for these.

  • FnOnce => move (like self)
  • FnMut => borrow mutably (like &mut self)
  • Fn => borrow immutably (like &self)