Избегайте одновременного заимствования изменяемого и неизменного

Чтобы добавить элементы двух Vecs, я написал такую ​​функцию, как

fn add_components(dest: &mut Vec<i32>, first: &Vec<i32>, second: &Vec<i32>){
  for i in 0..first.len() {
    dest[i] = first[i] + second[i];
  }
}

И это прекрасно работает, когда dest - это другой Vec.

let mut new_components = Vec::with_capacity(components.len());
Vector::add_components(&mut new_comps, &components, &other_components);

Но он взрывается, когда я пытаюсь добавить на месте:

Vector::add_components(&mut components, &components, &other_components);

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

Существуют ли какие-либо общепринятые и общие (не только в отношении Vecs) решения этой проблемы, которые не связаны с небезопасным кодом и магией указателей?

Другой пример этой проблемы:

Предположим, я хочу перегрузить AddAssign для числового типа, например

impl AddAssign<Output=&NumericType> for NumericType {
  fn add_assign(&mut self, other: &NumericType) {
    unimplemented!() // concrete implementation is not important
  }
}

Обратите внимание, что я хочу использовать ссылку в качестве второго аргумента, чтобы избежать копирования. Это отлично работает при добавлении двух разных объектов, но добавление объекта к самому себе создает точно такой же сценарий:

let mut num = NumericType{};
num += &num

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

Каковы лучшие практики (помимо копирования, конечно) для решения этой проблемы, которая возникает во многих формах?


person Marvin H.    schedule 04.07.2020    source источник
comment
Двойные запятые в списке параметров функции - это всего лишь опечатка, или вы действительно компилируете свой код, используя там двойные запятые?   -  person Bobulous    schedule 04.07.2020
comment
@Bobulous, это были опечатки!   -  person Marvin H.    schedule 04.07.2020
comment
Если «расширить» - это не то, что вы ищете, то я не могу понять, к чему вы стремитесь, даже с добавленным вами дополнительным примером. Единственный способ избежать копирования - либо стать владельцем источника, либо использовать ссылки (а использование ссылок в любом случае предполагает копирование). Может быть, кто-то еще может посоветовать, но это заставляет меня чесать затылок.   -  person Bobulous    schedule 04.07.2020
comment
@Bobulous В моем первом примере я явно пытаюсь заменить элементы Vec, что extend просто не делает. Но это также не относится к моему вопросу: что я могу сделать, когда функция принимает один и тот же объект в качестве изменяемого и неизменяемого аргумента? И когда ответ таков, что нет канонического обходного пути или что он включает указатели, тогда все будет в порядке.   -  person Marvin H.    schedule 05.07.2020


Ответы (1)


Для этого нет универсального решения. В общем, Rust не может абстрагироваться от изменчивости при проверке заимствований.

Вам потребуется две версии функции для локальной и целевой версии.

Rust имеет строгие правила псевдонима, поэтому dest[i] = first[i] + second[i] фактически компилируется в другой код в зависимости от того, есть ли у компилятора гарантия того, что dest и first различны. Не пытайтесь обмануть его с помощью unsafe, потому что это будет неопределенное поведение и будет неправильно скомпилировано.

person Kornel    schedule 13.07.2020
comment
Неправильно скомпилирован? Что именно это означает в данном контексте? Как мой пример может привести к машинному коду, который дает неправильный результат или даже дает сбой? - person Marvin H.; 13.07.2020
comment
UB означает, что компилятор может делать все, что захочет, включая носовых демонов, а UB не подлежит обсуждению. Нет никаких исключений, но в моем случае это не имеет значения! На практике строгие псевдонимы позволяют компилятору предположить, какая память не изменится, и переупорядочить операции чтения и записи по своему усмотрению. В нетривиальных случаях это может привести к другим результатам. Если вам не повезло, оптимизатор может увидеть, что вы обманываете строгий псевдоним, вывести его UB по определению, а код с UB не может существовать, поэтому он удалит код. - person Kornel; 19.07.2020