Ссылка / разыменование векторного элемента в цикле for - PullRequest
0 голосов
/ 06 сентября 2018

В приведенном ниже коде я хочу сохранить number_list после итерации по нему, поскольку .into_iter(), который for использует по умолчанию, будет потреблять. Таким образом, я предполагаю, что n: &i32, и я могу получить значение n путем разыменования.

fn main() {
    let number_list = vec![24, 34, 100, 65];
    let mut largest = number_list[0];

    for n in &number_list {
        if *n > largest {
            largest = *n;
        }
    }

    println!("{}", largest);
}

Мне было открыто, что вместо этого мы можем использовать &n в качестве «шаблона»:

fn main() {
    let number_list = vec![24, 34, 100, 65];
    let mut largest = number_list[0];

    for &n in &number_list {
        if n > largest {
            largest = n;
        }
    }

    println!("{}", largest);
    number_list;
}

Мое замешательство (и имейте в виду, что я не охватывал шаблоны) состоит в том, что я ожидал бы, что с n: &i32, затем &n: &&i32, а не с разрешением до значения (если двойная ссылка даже возможна). Почему это происходит, и значение & отличается в зависимости от контекста?

Ответы [ 3 ]

0 голосов
/ 06 сентября 2018

Это может помочь рассматривать ссылку как своего рода контейнер. Для сравнения рассмотрим Option, где мы можем «развернуть» значение, используя сопоставление с образцом, например, в операторе if let:

let n = 100;
let opt = Some(n);

if let Some(p) = opt {
    // do something with p
}

Мы называем Some и None конструкторами для Option, поскольку каждый из них выдает значение типа Option. Точно так же вы можете думать о & как о конструкторе для ссылки. И синтаксис симметричен:

let n = 100;
let reference = &n;

if let &p = reference {
    // do something with p
}

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

  1. if let, как указано выше

  2. match выражения:

    match opt {
        Some(1) => { ... },
        Some(p) => { ... },
        None    => { ... },
    }
    match reference {
        &1 => { ... },
        &p => { ... },
    }
    
  3. В аргументах функции:

    fn foo(&p: &i32) { ... }
    
  4. Loops:

    for &p in iter_of_i32_refs {
        ...
    }
    

И, вероятно, больше .

Обратите внимание, что последние два не будут работать для Option, потому что они будут паниковать, если будет найден None вместо Some, но это не может произойти со ссылками, потому что у них только один конструктор, &.

различается ли значение & в зависимости от контекста?

Надеюсь, если вы сможете интерпретировать & как конструктор вместо оператора, то вы увидите, что его значение не меняется. Это довольно интересная особенность Rust - вы можете использовать конструкторы в правой части выражения для создания значений и в левой части для их разделения (деструктурирование).

0 голосов
/ 06 сентября 2018

Мое замешательство (и имейте в виду, что я не охватывал шаблоны) заключается в том, что я ожидаю, что, поскольку n: & i32, тогда & n: && i32, а не разрешение до значения (если двойная ссылка даже возможна). Почему это происходит, и значение & отличается в зависимости от контекста?

Когда вы делаете сопоставление с образцом (например, когда пишете for &n in &number_list), вы не говорите, что n - это &i32, вместо этого вы говорите, что &n (шаблон) - это &i32 (выражение), из которого компилятор делает вывод, что n является i32.

Подобные вещи случаются для всех видов шаблонов, например, при сопоставлении с шаблоном в if let Some (x) = Some (42) { /* … */ } мы говорим, что Some (x) равно Some (42), поэтому x равно 42.

0 голосов
/ 06 сентября 2018

В отличие от других языков (C ++), &n в этом случае - не ссылка, а сопоставление с образцом, что означает, что это ожидание ссылки.
Противоположностью этому будет ref n, что даст вам &&i32 как тип.

Это также относится и к крышкам, например,

(0..).filter(|&idx| idx < 10)...

Обратите внимание, что это переместит переменную, например, Вы не можете сделать это с типами, которые не реализуют черту Copy.

...