Как применить функцию к каждому элементу вектора в Rust (аналог map)?
Правильный путь: Итераторы и метод .iter()
Ваша ошибка типична для новичков. Дело в том, что Vec сам по себе не имеет метода map. В Rust функциональная обработка коллекций построена на концепции итераторов (типаж Iterator). Чтобы применить map, нужно сначала создать итератор из вектора.
Вот исправленный код, который делает то, что вы хотите:
fn main() {
let numbers = vec![1, 2, 3, 4];
// Шаг 1: .iter() создаёт итератор, который заимствует элементы (&i32)
// Шаг 2: .map() применяет замыкание к каждому элементу
// Шаг 3: .collect() собирает результат обратно в Vec
let squared: Vec<i32> = numbers.iter().map(|x| x * x).collect();
println!("{:?}", squared); // Выведет: [1, 4, 9, 16]
}
Три способа создания итератора
В зависимости от того, хотите ли вы владеть данными или только читать их, используйте разные методы:
.iter()— заимствует элементы.xв замыкании будет&i32(ссылку). Если вам нужно вернуть новый вектор, вы можете разыменоватьxчерез*xили просто выполнить арифметику, так как Rust сам разыменовывает для операторов..into_iter()— потребляет вектор (забирает владение).xбудетi32(значение). После этого исходный вектор использовать нельзя..iter_mut()— заимствует с возможностью изменения.xбудет&mut i32. Используется, если вы хотите изменить исходный вектор на месте, а не создавать новый.
Пример с into_iter() (ваш случай из Python)
Если вам не нужен старый вектор после преобразования, используйте into_iter(). Это будет ближе всего к вашему Python-коду:
fn main() {
let numbers = vec![1, 2, 3, 4];
let squared: Vec<i32> = numbers.into_iter().map(|x| x * x).collect();
// numbers больше не доступен, так как мы передали владение
println!("{:?}", squared);
}
Важные нюансы для новичков
- Ленивость:
mapв Rust — ленивый. Он не выполнит замыкание, пока вы не вызовете.collect(),.for_each()или не проитерируетесь в циклеfor. - Тип замыкания: Замыкание
|x| x * xдолжно возвращать значение того же типа, который ожидает итоговый вектор. В нашем случаеi32. - Производительность: Компилятор Rust (LLVM) генерирует очень эффективный код. Цепочки итераторов (
iter().map().filter().collect()) часто работают так же быстро, как ручные циклы, а иногда и быстрее.
Полный пример с фильтрацией
Для полноты картины покажу, как совместить map и filter (как в Python list comprehension):
fn main() {
let numbers = vec![1, 2, 3, 4, 5, 6];
// Возвести в квадрат только чётные числа
let result: Vec<i32> = numbers
.iter()
.filter(|&&x| x % 2 == 0) // Фильтр: оставляем только чётные
.map(|x| x * x) // Преобразование
.collect();
println!("{:?}", result); // [4, 16, 36]
}
Обратите внимание на двойное && в фильтре: numbers.iter() даёт нам &i32, а замыкание фильтра получает ссылку на элемент итератора, то есть &&i32. Это нормально, компилятор подскажет.
Альтернативный подход: Использование циклов for
Если функциональный стиль с map и замыканиями кажется вам слишком сложным на первых порах, вы можете использовать обычный цикл for и изменяемый вектор. Это более императивный стиль, который может быть понятнее для начинающих.
Вот как можно решить ту же задачу без итераторов:
fn main() {
let numbers = vec![1, 2, 3, 4];
let mut squared = Vec::new(); // Создаём пустой изменяемый вектор
for &num in numbers.iter() { // Итерируемся по ссылкам
squared.push(num * num); // Добавляем квадрат в новый вектор
}
println!("{:?}", squared); // [1, 4, 9, 16]
}
Когда это удобно? Если логика преобразования сложная (много условий, вложенные циклы, мутация внешних переменных), то цикл for может быть читаемее, чем цепочка итераторов с сложными замыканиями.
Недостаток: Вам нужно вручную объявлять mut и следить за типом коллекции. Компилятор Rust всё равно оптимизирует цикл, но код получается более многословным по сравнению с map.
Совет: Начните с циклов для понимания логики, а затем постепенно переходите на итераторы — это позволит писать более декларативный и безопасный код.
Ответов пока нет.
Войдите чтобы оставить ответ