Какой идиоматический способ добавить срез к вектору?

У меня есть фрагмент &[u8], и я хотел бы добавить его к Vec<u8> с минимальным копированием. Вот два подхода, которые я знаю:

let s = [0u8, 1u8, 2u8];
let mut v = Vec::new();
v.extend(s.iter().map(|&i| i));
v.extend(s.to_vec().into_iter()); // allocates an extra copy of the slice

Есть ли лучший способ сделать это в Rust stable? (rustc 1.0.0-beta.2)

Ответы

Ответ 1

Есть способ, который выполняет именно это: Vec::extend_from_slice

Пример:

let s = [0u8, 1, 2];
let mut v = Vec::new();
v.extend_from_slice(&s); 

Ответ 2

v.extend(s.iter().cloned());

Это эквивалентно использованию .map(|&i| i) и минимальному копированию.

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

Теперь, при этом, нужно рассмотреть две вещи:

  • Ржавчина имеет тенденцию к inline довольно агрессивно; в этом коде достаточно информации для компилятора, чтобы просто скопировать значения непосредственно в пункт назначения без какого-либо промежуточного шага.

  • Закрытие в Rust не похоже на закрытие на большинстве других языков: они не требуют выделения кучи и могут быть непосредственно встроены, что делает их не менее эффективными, чем жесткое кодирование поведения напрямую.

Имейте в виду, что вышеупомянутые два зависят от оптимизации: они, как правило, работают лучше, но не гарантируются.


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

Ответ 3

Я не могу говорить о полном влиянии на производительность, но v + &s будет работать на бета-версии, что, я считаю, просто похоже на то, что каждое значение добавляется к исходному Vec.