Rust – il linguaggio – 9

t3

Oggi qui: /usr/local/share/doc/rust/html/book/references-and-borrowing.html, continuando da qui.

References and Borrowing

Non traduco tanto quello si usa 🙄
This guide is two of three presenting Rust’s ownership system. Come detto nel post appena citato.

Meta

Importante quello che dice ma è la ripetizione di quello già detto. OK, salto, tanto i post vanno letti in sequenza, si sa.
With that in mind, let’s learn about borrowing.

Borrowing

At the end of the ownership [sì, sempre quel post] section, we had a nasty function that looked like this:

fn foo(v1: Vec, v2: Vec) -> (Vec, Vec, i32) {
    // do stuff with v1 and v2

    // hand back ownership, and the result of our function
    (v1, v2, 42)
}
let v1 = vec![1, 2, 3];
let v2 = vec![1, 2, 3];
let (v1, v2, answer) = foo(v1, v2);

This is not idiomatic Rust, however, as it doesn’t take advantage of borrowing. Here’s the first step (l’ho modificato per togliere i warnings):

fn main() {
    fn foo(v1: &Vec, v2: &Vec) -> i32 {
        // do stuff with v1 and v2
        let x = v1[1] + v2[2];
        println!("x vale {}", x);
        // return the answer
        42
    }
    
    let v1 = vec![1, 2, 3];
    let v2 = vec![1, 2, 3]; 
    let answer = foo(&v1, &v2); // we can use v1 and v2 here!
    println!("La risposta è {}", answer);
}

rs9-0Instead of taking Vec<i32>s as our arguments, we take a reference: &Vec<i32>. And instead of passing v1 and v2 directly, we pass &v1 and &v2. We call the &T type a ‘reference’, and rather than owning the resource, it borrows ownership. A binding that borrows something does not deallocate the resource when it goes out of scope. This means that after the call to foo(), we can use our original bindings again.

References are immutable, just like bindings. This means that inside of foo(), the vectors can’t be changed at all:

fn foo(v: &Vec) {
     v.push(5);
}
let v = vec![];
foo(&v);

errors with: error: cannot borrow immutable borrowed content `*v` as mutable v.push(5);

Pushing a value mutates the vector, and so we aren’t allowed to do it.

&mut references

There’s a second kind of reference: &mut T. A ‘mutable reference’ allows you to mutate the resource you’re borrowing. For example:

fn main() {
    let mut x = 5;
    {
        let y = &mut x;
        *y += 1;
    }
    println!("{}", x);
}

rs9-1We make y a mutable reference to x, then add one to the thing y points at. You’ll notice that x had to be marked mut as well, if it wasn’t, we couldn’t take a mutable borrow to an immutable value.

You’ll also notice we added an asterisk (*) in front of y, making it *y, this is because y is an &mut reference. You’ll also need to use them for accessing the contents of a reference as well.

Otherwise, &mut references are just like references. There is a large difference between the two, and how they interact, though. You can tell something is fishy in the above example, because we need that extra scope, with the { and }. If we remove them, we get an error:

error: cannot borrow `x` as immutable because it is also borrowed as mutable
    println!("{}", x);
                   ^

note: previous borrow of `x` occurs here; the mutable borrow prevents subsequent moves, borrows, or modification of `x` until the borrow ends let y = &mut x;.

note: previous borrow ends here
fn main() {

}
^

As it turns out, there are rules. 🙄 Logico ma pistino assay! 🙄

Le regole

The rules dice lui.
First, any borrow must last for a scope no greater than that of the owner. Second, you may have one or the other of these two kinds of borrows, but not both at the same time:

  • one or more references (&T) to a resource,
  • exactly one mutable reference (&mut T).

You may notice that this is very similar, though not exactly the same as, to the definition of a data race:

There is a ‘data race’ when two or more pointers access the same memory location at the same time, where at least one of them is writing, and the operations are not synchronized.

With references, you may have as many as you’d like, since none of them are writing. If you are writing, you need two or more pointers to the same memory, and you can only have one &mut at a time. This is how Rust prevents data races at compile time: we’ll get errors if we break the rules.
Qui mi sa che rischio di dimenticarmelo 🙄 anche se è tremendamente logico 😀

With this in mind, let’s consider our example again.

Pensare per scopes

Here’s the code:

let mut x = 5;
let y = &mut x;

*y += 1;

println!("{}", x);

This code gives us this error: error: cannot borrow `x` as immutable because it is also borrowed as mutable println!("{}", x);

This is because we’ve violated the rules: we have a &mut T pointing to x, and so we aren’t allowed to create any &Ts. One or the other. The note hints at how to think about this problem: l’indicatore di dov’è l’errore (il segno ^) si trova sotto la } finale.

In other words, the mutable borrow is held through the rest of our example. What we want is for the mutable borrow to end before we try to call println! and make an immutable borrow. In Rust, borrowing is tied to the scope that the borrow is valid for. And our scopes look like this:

let mut x = 5;
let y = &mut x;    // -+ &mut borrow of x starts here
                   //  |
*y += 1;           //  |
                   //  |
println!("{}", x); // -+ - try to borrow x here
                   // -+ &mut borrow of x ends here

The scopes conflict: we can’t make an &x while y is in scope.

So when we add the curly braces:

let mut x = 5;
{
    let y = &mut x; // -+ &mut borrow starts here
    *y += 1;        //  |
}                   // -+ ... and ends here

println!("{}", x);  // <- try to borrow x here

There’s no problem. Our mutable borrow goes out of scope before we create an immutable one. But scope is the key to seeing how long a borrow lasts for.

Problemi che il borrowing previene

Why have these restrictive rules? Well, as we noted, these rules prevent data races. What kinds of issues do data races cause? Here’s a few.

invalidazione di iteratori
One example is ‘iterator invalidation’, which happens when you try to mutate a collection that you’re iterating over. Rust’s borrow checker prevents this from happening:

fn main() {
    let v = vec![1, 2, 3];
    for i in &v {
        println!("{}", i);
    }
}

rs9-2As we iterate through the vectors, we’re only given references to the elements. And v is itself borrowed as immutable, which means we can’t change it while we’re iterating:

fn main() {
    let mut v = vec![1, 2, 3];
    for i in &v {
        println!("{}", i);
        v.push(34);
    }
}

rs9-3We can’t modify v because it’s borrowed by the loop.

uso dopo il free (o devo dire deallocazione?)
References must not live longer than the resource they refer to. Rust will check the scopes of your references to ensure that this is true.
If Rust didn’t check this property, we could accidentally use a reference which was invalid. For example:

fn main() {
    let y: &i32;
    {
        let x = 5;
        y = &x;
    }
    println!("{}", y);

rs9-4y is only valid for the scope where x exists. As soon as x goes away, it becomes invalid to refer to it. As such, the error says that the borrow ‘doesn’t live long enough’ because it’s not valid for the right amount of time.
The same problem occurs when the reference is declared before the variable it refers to. This is because resources within the same scope are freed in the opposite order they were declared:

fn main() {
    let y: &i32;
    let x = 5;
    y = &x;
    println!("{}", y);

rs9-5In the above example, y is declared before x, meaning that y lives longer than x, which is not allowed.

Pensierino della sera (anche se non è l’ora): da sempre * e & sono –come dire… 🙄

:mrgreen:

Posta un commento o usa questo indirizzo per il trackback.

Trackback

Rispondi

Inserisci i tuoi dati qui sotto o clicca su un'icona per effettuare l'accesso:

Logo di WordPress.com

Stai commentando usando il tuo account WordPress.com. Chiudi sessione /  Modifica )

Google photo

Stai commentando usando il tuo account Google. Chiudi sessione /  Modifica )

Foto Twitter

Stai commentando usando il tuo account Twitter. Chiudi sessione /  Modifica )

Foto di Facebook

Stai commentando usando il tuo account Facebook. Chiudi sessione /  Modifica )

Connessione a %s...

Questo sito utilizza Akismet per ridurre lo spam. Scopri come vengono elaborati i dati derivati dai commenti.

%d blogger hanno fatto clic su Mi Piace per questo: