Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@IBUzPE9
Last active November 24, 2016 16:37
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save IBUzPE9/0ed95d95da1d0dcbf03caf8c442fa06b to your computer and use it in GitHub Desktop.
Save IBUzPE9/0ed95d95da1d0dcbf03caf8c442fa06b to your computer and use it in GitHub Desktop.

С первым примером все просто:

struct Test;

fn main() {
    let t = &mut Test;
    {
        let x = t;
    }
    let y = t;
}

Так как тип &mut Test некопируемый, t перемещается в x, затем x дропается, в последней строке получаем ошибку - использование перемещенной переменной t.

Второй пример хитрее:

struct Test;

fn main() {
    let t = &mut Test;
    {
        let x:&mut Test = t;
    }
    let y = t;
}

Теперь явно указан тип временной переменной x. Но это в корне все меняет - пример нормально компилируется и запускается.

Отчасти ответ дан в Растономиконе в главе про живучесть(Liveness) ссылок (перевод). Там описывается механизм под названием передача заимствования (reborrowing).

Вот пример оттуда, который отлично все объясняет:

let x = &mut (1, 2);
{
    // reborrow x to a subfield
    let y = &mut x.0;
    // y is now live, but x isn't
    *y = 3;
}
// y goes out of scope, so x is live again
*x = (5, 7);

Наш пример эквивалентен такому коду:

struct Test;

fn main() {
    let t = &mut Test;
    {
        //reborrow t
        let x = &mut*t; 
        // x is now live, but t isn't
    }
    // x goes out of scope, so t is live again
    let y = t;
}

Отличие только в том, что у нас происходит передача заимствования на всю структуру, а не на ее часть.

Похоже на взятие мутабельной ссылки на мутабельную ссылку, но в результате получаем копию исходной ссылки а не ссылку на ссылку. Довольно удобно на самом деле.

Почему же указание типа переменной приводит к неявному включению механизма передачи заимствования:

let x:&mut Test = t; //implicit reborrowing

Подозреваю, что это сделано для того, чтобы при передаче параметров в функцию по мутабельной ссылке нам не приходилось каждый раз повторять этот замечательный шаблон: &mut*t.

Т.е. такой код будет нормально работать:

fn mut_some<T>(_:&mut T) { /*something usefull*/ }

let t = &mut Test;

mut_some(t); //implicit reborrowing
mut_some(t);
mut_some(t);

А вот такой - нет:

fn move_some<T>(_:T) { /*something usefull*/ }

let t = &mut Test;

move_some(t); //no implicit reborrowing
move_some(t);
move_some(t);

Первый же вызов move_some(t) "съедает" переменную t.

Придется сделать явную передачу заимствования:

fn move_some<T>(_:T) { /*something usefull*/ }

let t = &mut Test;

move_some(&mut*t); //explicit reborrowing
move_some(&mut*t);
move_some(&mut*t);
@vldm
Copy link

vldm commented Nov 24, 2016

Может всё связано с неявным выводом лайфтайма, ведь переменная в внутреннем скопе с новым лайфтаймом, а значит их типы отличаются (хоть и по лайфтайму) с внешней переменной, непонятно только какой лайфтайм будет у переменной, если тип не указан, такой же как у внешней переменной? Нужно будет посмотреть разницу в mir'e сгенерированном. А еще, если все из-за разных типов(по лайфтайму), то в компилятор есть более общий механизм: Deref coercion, про который в книге написано, что компилятор добавит столько * сколько нужно, чтобы привести к нужному типу.

@IBUzPE9
Copy link
Author

IBUzPE9 commented Nov 24, 2016

Ну если убрать внутренний блок (фигурные скобки), то оба примера будут выдавать ошибки. Первый - use of moved value: t, второй - cannot move out of t because it is borrowed. Т.е. все останется как раньше. Хотя даже без внутреннего scope лайфтаймы будут разные. Можно явно задать лайфтайм ссылке?

Да про Deref coercion идея интересная. Но тут откуда то должен взяться еще и &mut.

Вообще странно, что такие загадочные свойства мутабельных ссылок так скромно обошли вниманием даже в Растономиконе.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment