Как инициализировать массив и сохранить ссылки на его элементы? - PullRequest
2 голосов
/ 15 марта 2020
let mut u:[usize;3] = [0;3];
let mut v = vec![];
for i in 0..3 {
    u[i] = i;
    let e = &(u[i]);
    v.push(e);
}
error[E0506]: cannot assign to `u[_]` because it is borrowed
 --> src/lib.rs:5:9
  |
5 |         u[i] = i;
  |         ^^^^^^^^ assignment to borrowed `u[_]` occurs here
6 |         let e = &(u[i]);
  |                 ------- borrow of `u[_]` occurs here
7 |         v.push(e);
  |         - borrow later used here

( Детская площадка )

Я понимаю, что компилятор спасает меня от себя, но я действительно хочу это поведение, и я не хочу его принудительно через unsafe, как я уверен, облажаться. Какие у меня варианты?

У меня такое ощущение, что это как-то связано с жизнями, которые мне еще предстоит понять. Возможно, это связано с наличием двух владельцев для одних и тех же данных? Я не думаю, что это должно было быть проблемой, пока ты пережил v.

Как я могу оправиться от этого? Или это просто запрещено из-за нескольких владельцев, и поэтому мне нужно переосмыслить свой подход?

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

Ответы [ 2 ]

3 голосов
/ 16 марта 2020

Итераторы приходят вам на помощь!

Поскольку компилятор не может рассуждать с кодом в вашем l oop, вам необходимо наложить ограничения в виде системы типов и требований времени жизни. Это достигается с помощью итераторов:

let mut u: [usize; 3] = [0; 3];
let mut v: Vec<&usize> = vec![];
u
    .iter_mut()
    .enumerate()
    .for_each(|(idx, val)| {
        *val = idx;
        v.push(&*val);
    });

Детская площадка .

По запросу; вот что это делает:

  • u.iter_mut() происходит от того факта, что массив приводит к срезу , а срезы имеют iter_mut функция. Этот итератор дает &mut T.

  • .enumerate(), принимает итератор и выполняет удар по индексу. Ваш элемент итератора преобразуется следующим образом: T переходит к (usize, T).

  • .for_each(f) полностью очищает итератор и запускает функцию для каждого элемента (f - это функция) , f в этом случае должна иметь подпись вызова fn(value: (usize, &mut T)) -> ().

  • Мы передаем замыкание в вызов for_each, и это важно: замыкание может перехватывать перехват v. Это позволяет модифицировать v, и поэтому мы можем сказать v.push в закрытии.

Почему это работает , а ваше - :

  • У вас есть равнина для l oop. For for l oop может делать все что угодно, и компилятор не приложит усилий для проверки того, что вы делаете. Он просто удостоверится, что то, что вы делаете, не является незаконным, согласно компилятору, и будет консервативен в этом.

  • Это, с другой стороны, использует семантику закрытие, чтобы гарантировать, что вы не можете изменить u любым другим способом, кроме как через элемент, который вы видите прямо сейчас. Компилятор видит, что ссылки на каждый элемент u являются уникальными, поскольку исходная ссылка на u была уникальной, и поэтому у него нет проблем с этим. Тогда время жизни заимствований (или ссылок, того же термина) в v совпадает с временем жизни u, и поэтому делается вывод, что v заимствует из u элегантным образом.

  • Это было бы нелегко с помощью l oop, поскольку компилятору пришлось бы буквально отслеживать строку кода по вызову, строка за строкой, чтобы определить это иначе. Это был бы рассол. И это маринованное, так что он просто плюет на вас, потому что это будет слишком сложно доказать в более сложных случаях.

2 голосов
/ 16 марта 2020

Вы можете разделить l oop на два прохода. Инициализируйте записи, затем возьмите ссылку на них:

    let mut u:[usize;3] = [0;3];
    let mut v = vec![];

    for i in 0..3 {
        u[i] = i;
    }

    for i in 0..3 {
        let e = &(u[i]);
        v.push(e);
    }

Это работает, потому что вам больше не нужна изменяемая ссылка на u после завершения инициализации l oop.

У меня есть ощущение, что это как-то связано с жизнями, которые мне еще предстоит понять. Возможно, это связано с наличием двух владельцев для одних и тех же данных? Я не думаю, что это должно быть проблемой, пока вы пережили v.

Время жизни u и v не является проблемой, проблема заключается в том, чтобы ссылаться на изменяемую структуру.

...