Как создать вектор нулей заданного размера в Rust?
Правильный способ: макрос vec! с повторителем
То, что вы написали — vec![0; size] — это абсолютно правильный и идиоматичный способ создать вектор из N нулей в Rust. Этот синтаксис называется «повторитель» (repeat expression) внутри макроса vec!. Он работает для любого типа, который реализует типаж Clone (а все примитивные типы вроде i32, f64, bool его реализуют).
let size = 10;
let zeros: Vec<i32> = vec![0; size];
println!("{:?}", zeros); // [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
println!("Длина: {}", zeros.len()); // 10
Как это работает под капотом?
Макрос vec![value; n] разворачивается в следующий код (упрощённо):
let mut vec = Vec::with_capacity(n);
vec.resize(n, value);
vec
То есть он:
- Выделяет память под N элементов (без лишнего резерва).
- Заполняет все N позиций копированием
valueс помощьюClone. - Устанавливает длину вектора равной N.
Это значит, что после vec![0; size] вы можете сразу обращаться к любому элементу по индексу — zeros[0], zeros[size-1] — без риска паники.
Альтернативные подходы и их особенности
Вот несколько других способов, которые могут быть полезны в разных ситуациях:
- Метод
repeatиз итераторов:
Этот способ более гибкий, если вам нужно не просто повторить одно значение, а, например, сгенерировать последовательность. Но для простого «вектора нулей» он избыточен и может быть чуть медленнее из-за динамической коллекции.let zeros: Vec<i32> = std::iter::repeat(0).take(size).collect(); - Метод
resizeдля существующего вектора:
Полезно, если вы сначала создаёте пустой вектор с запасом, а потом точно знаете, какой размер ему нужен.let mut zeros = Vec::with_capacity(size); zeros.resize(size, 0); - Метод
new+ цикл (НЕ РЕКОМЕНДУЕТСЯ):
Этот способ работает, но он менее эффективен, так какlet mut zeros = Vec::new(); for _ in 0..size { zeros.push(0); }pushможет вызывать несколько перераспределений памяти, если не вызватьwith_capacityзаранее.
Производительность: что выбрать?
Для большинства случаев используйте vec![0; size] — это самый короткий, понятный и эффективный вариант. Компилятор Rust (особенно с оптимизациями) часто превращает его в вызов memset или аналогичный низкоуровневый код, что даёт производительность, близкую к C.
Если вы работаете с очень большими векторами (миллионы элементов) и хотите избежать лишней инициализации (например, вы всё равно планируете перезаписывать все элементы сразу после создания), можно использовать Vec::with_capacity(size) и заполнять его вручную. Но будьте осторожны: обращение к неинициализированным элементам через [] вызовет панику, так как длина вектора равна 0.
// Безопасный способ для отложенной инициализации:
let mut zeros = vec![0; size]; // инициализируем нулями
// ... потом быстро перезаписываем в цикле
Важное замечание про типы
Повторитель работает только для типов, реализующих Clone. Если вам нужно создать вектор из значений, которые не являются Clone (например, строк String или пользовательских структур), используйте итератор или цикл с явным созданием каждого элемента.
// Для String нужно создавать каждый элемент отдельно:
let strings: Vec<String> = (0..size).map(|_| String::new()).collect();
В итоге, для вашей задачи с вектором нулей используйте vec![0; size] — это и есть «стандартный» способ в Rust.
Дополнение: безопасная работа с неинициализированной памятью
Если вас беспокоит производительность инициализации нулями для очень больших векторов (например, 10 миллионов элементов), и вы точно знаете, что заполните все элементы вручную, можно использовать Vec::with_capacity вместе с unsafe методом set_len. Но этот подход требует осторожности!
let size = 10_000_000;
let mut zeros: Vec<i32> = Vec::with_capacity(size);
// ПРЕДУПРЕЖДЕНИЕ: это unsafe, так как мы говорим Rust, что память инициализирована
unsafe {
zeros.set_len(size);
}
// Теперь zeros.len() == size, но элементы содержат "мусор"!
// Нужно немедленно заполнить их:
for x in zeros.iter_mut() {
*x = 0; // или любое другое значение
}
Этот способ может быть быстрее, если вы всё равно собираетесь перезаписывать все элементы, потому что избегает двойной записи (сначала ноль, потом ваше значение). Однако я не рекомендую его новичкам — легко ошибиться и получить undefined behavior. Для 99% случаев vec![0; size] работает отлично.
Также есть крейт arrayvec (из внешних зависимостей), который предоставляет массивы фиксированного размера на стеке с похожей функциональностью, но это уже выходит за рамки стандартной библиотеки.