Borrowing in action

Rust's error diagnostics around the borrowing rules are really helpful when we go against the borrow checker. In the following few examples, we'll see them in various contexts.

Borrowing in functions: As you saw previously, moving ownership when making function calls does not make much sense if you are only reading the value, and is very limiting. You don't get to use the variable after you call the function. Instead of taking parameters by value, we can take them by references. We can fix the previous code example that was presented in the ownership section to pass the compiler without cloning, like so:

// borrowing_functions.rs

fn take_the_n(n: &mut u8) {
*n += 2;
}

fn take_the_s(s: &mut String) {
s.push_str("ing");
}

fn main() {
let mut n = 5;
let mut s = String::from("Borrow");

take_the_n(&mut n);
take_the_s(&mut s);

println!("n changed to {}", n);
println!("s changed to {}", s);
}

In the preceding code, take_the_s and take_the_n now take mutable references. With this, we needed to modify three things in our code. First, the variable binding will have to be made mutable:

let mut s = String::from("Borrow"); 

Second, our function changes to the following:

fn take_the_s(n: &mut String) { 
s.push_str("ing");
}

 

Third, the call site would also need to change to this form:

    take_the_s(&mut s); 

Again, we can see that everything in Rust is explicit. Mutability is very visible in Rust code for obvious reasons, especially when multiple threads come into play.

Borrowing in match: In match expressions, a value is moved by default in the match arms, unless it's a Copy type. The following code, which was presented in the previous section on ownership, compiles by borrowing in match arms:

// borrowing_match.rs

#[derive(Debug)]
enum Food {
Cake,
Pizza,
Salad
}

#[derive(Debug)]
struct Bag {
food: Food
}

fn main() {
let bag = Bag { food: Food::Cake };
match bag.food {
Food::Cake => println!("I got cake"),
ref a => println!("I got {:?}", a)
}

println!("{:?}", bag);
}

We made a slight change to the preceding code, which might be familiar to you from the ownership section. For the second match arm, we prefixed a with ref. The ref keyword is a keyword that can match items by taking a reference to them instead of capturing them by value. With this change, our code compiles.

Returning a reference from a function: In the following code example, we have a function that tries to return a reference to a value declared within the function:

// return_func_ref.rs

fn get_a_borrowed_value() -> &u8 {
let x = 1;
&x
}

fn main() {
let value = get_a_borrowed_value();
}

This code fails to pass the borrow checker, and we are met with the following error:

The error message says that we are missing a lifetime specifier. That doesn't help much in regards to explaining what is wrong with our code. This is where we need to acquaint ourselves with the concept of lifetimes, which we will cover in the next section. Before that, let's expound on the kind of functions that we can can have based on borrowing rules.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset