Как я могу скопировать вектор в другое место и повторно использовать существующую выделенную память? - PullRequest
6 голосов
/ 06 января 2020

В C ++ для копирования содержимого вектора в другой вектор мы используем оператор присваивания dest = src. Однако в Rust src будет перемещен в dest и больше не будет использоваться.

Я знаю, что самый простой ответ - сделать dest = src.clone() (ради этого вопроса мы примем T в Vec<T> это Clone). Однако - если я правильно понимаю - это создает новый третий вектор с скопированным содержимым src и перемещает его в dest, отбрасывая динамически распределенный массив dest. Если это правильно, это абсолютно ненужное динамическое распределение c, когда мы могли бы просто скопировать содержимое непосредственно в dest (при условии, что оно имеет достаточную емкость).

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

// copy contents of src to dest without just cloning src
fn copy_content<T: Clone>(dest: &mut Vec<T>, src: &Vec<T>) {
    dest.clear();
    if dest.capacity() < src.len() {
        dest.reserve(src.len());
    }
    for x in src {
        dest.push(x.clone());
    }
}

Есть ли способ сделать это с помощью встроенных или стандартных библиотечных утилит? Оптимизирован ли dest = src.clone() компилятором для этого?

Я знаю, что если T имеет динамические c ресурсы, то дополнительное выделение из src.clone() не имеет большого значения, но если T, например, i32 или любой другой тип Copy, тогда он вызывает выделение, где ничего не требуется.

1 Ответ

10 голосов
/ 06 января 2020

Вы когда-нибудь смотрели на определение Clone? У него есть хорошо известный clone метод, но также полезный, но часто забываемый clone_from метод :

pub trait Clone : Sized {
    fn clone(&self) -> Self;
    fn clone_from(&mut self, source: &Self) {
        *self = source.clone()
    }
}

Цитируя do c:

Выполняет копирование из источника.

a.clone_from(&b) эквивалентен a = b.clone() по функциональности, но может быть переопределен для повторного использования ресурсов a во избежание ненужных выделений.

Конечно, тип, такой как Vec, не использует предоставленный по умолчанию clone_from и определяет своим собственным более эффективным способом, аналогичным тому, который вы получили бы в C ++ из запись dest = src:

fn clone_from(&mut self, other: &Vec<T>) {
    other.as_slice().clone_into(self);
}

с [T]::clone_into, определяемым как :

fn clone_into(&self, target: &mut Vec<T>) {
    // drop anything in target that will not be overwritten
    target.truncate(self.len());
    let len = target.len();

    // reuse the contained values' allocations/resources.
    target.clone_from_slice(&self[..len]);

    // target.len <= self.len due to the truncate above, so the
    // slice here is always in-bounds.
    target.extend_from_slice(&self[len..]);
}
...