Как отсортировать вектор в Rust: полное руководство с примерами
Основы сортировки векторов в Rust
Rust предоставляет два основных метода для сортировки векторов: sort() и sort_unstable(). Оба метода изменяют вектор на месте (in-place) и требуют, чтобы элементы реализовывали трейт Ord.
Начнём с простого примера — сортировка чисел по возрастанию:
let mut numbers = vec![5, 2, 8, 1, 9, 3];
numbers.sort();
println!("{:?}", numbers); // [1, 2, 3, 5, 8, 9]
Ошибка, которую вы получили, скорее всего, связана с тем, что вы пытались отсортировать ссылку на вектор, а не сам вектор. Убедитесь, что вектор объявлен как mut и вы вызываете метод на самом векторе, а не на ссылке.
Сортировка строк
Со строками всё аналогично, но есть нюанс: Rust сортирует строки лексикографически (по Unicode-кодовым точкам):
let mut fruits = vec!["banana", "apple", "cherry", "date"];
fruits.sort();
println!("{:?}", fruits); // ["apple", "banana", "cherry", "date"]
Если вам нужна сортировка с учётом локали (например, для русского языка), используйте крейт unicode_collation.
Сортировка по убыванию
Для сортировки по убыванию используйте метод reverse() после обычной сортировки или метод sort_by() с кастомным компаратором:
let mut numbers = vec![5, 2, 8, 1, 9, 3];
numbers.sort_by(|a, b| b.cmp(a));
println!("{:?}", numbers); // [9, 8, 5, 3, 2, 1]
Или более элегантный способ с sort_unstable_by():
numbers.sort_unstable_by(|a, b| b.cmp(a));
Сортировка пользовательских структур
Для сортировки структур нужно реализовать трейт Ord или использовать sort_by():
#[derive(Debug)]
struct Person {
name: String,
age: u32,
}
let mut people = vec![
Person { name: "Alice".to_string(), age: 30 },
Person { name: "Bob".to_string(), age: 25 },
Person { name: "Charlie".to_string(), age: 35 },
];
// Сортировка по возрасту
people.sort_by(|a, b| a.age.cmp(&b.age));
println!("{:?}", people);
// [Person { name: "Bob", age: 25 }, Person { name: "Alice", age: 30 }, Person { name: "Charlie", age: 35 }]
Если вы хотите сортировать структуры по умолчанию, реализуйте трейты Eq, PartialEq, Ord и PartialOrd:
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd)]
struct Person {
name: String,
age: u32,
}
// Теперь можно сортировать просто .sort()
people.sort();
// Сортировка сначала по name, затем по age
sort() vs sort_unstable()
Основные различия:
- sort() — стабильная сортировка (merge sort). Сохраняет порядок равных элементов. Требует O(n) дополнительной памяти.
- sort_unstable() — нестабильная сортировка (quicksort). Не гарантирует порядок равных элементов. Не требует дополнительной памяти (сортирует на месте).
В большинстве случаев используйте sort_unstable() — он быстрее и не выделяет память. Стабильная сортировка нужна только если важен порядок равных элементов.
Сортировка с кастомным компаратором
Методы sort_by() и sort_unstable_by() принимают замыкание-компаратор, которое должно возвращать Ordering:
let mut numbers = vec![5, 2, 8, 1, 9, 3];
// Чётные числа перед нечётными
numbers.sort_by(|a, b| {
let a_even = a % 2 == 0;
let b_even = b % 2 == 0;
if a_even == b_even {
a.cmp(b) // внутри групп сортируем по возрастанию
} else if a_even {
std::cmp::Ordering::Less // чётные идут первыми
} else {
std::cmp::Ordering::Greater
}
});
println!("{:?}", numbers); // [2, 8, 1, 3, 5, 9]
Также есть удобный метод sort_by_key(), если компаратор можно выразить через простое преобразование:
// Сортировка строк по длине
let mut words = vec!["rust", "python", "c", "java", "typescript"];
words.sort_by_key(|s| s.len());
println!("{:?}", words); // ["c", "rust", "java", "python", "typescript"]
Надеюсь, это помогло! Rust даёт гибкие инструменты для сортировки, главное — понять разницу между стабильной и нестабильной версиями.
<<Альтернативный подход: использование итераторов и collect()
Иногда вам не нужно изменять исходный вектор, а достаточно получить отсортированную копию. В таких случаях удобно использовать итераторы:
let numbers = vec![5, 2, 8, 1, 9, 3];
let sorted_numbers: Vec<i32> = numbers.iter().copied().collect::<Vec<_>>().sort();
// Или более идиоматично:
let sorted_numbers = {
let mut v = numbers.clone();
v.sort();
v
};
Но ещё лучше — использовать метод sort_unstable() с последующим клонированием, если нужно сохранить оригинал.
Сортировка с помощью крейта itertools
Крейт itertools предоставляет метод sorted(), который работает с любым итератором:
use itertools::Itertools;
let numbers = vec![5, 2, 8, 1, 9, 3];
let sorted_numbers: Vec<i32> = numbers.into_iter().sorted().collect();
println!("{:?}", sorted_numbers); // [1, 2, 3, 5, 8, 9]
Это удобно, когда вы работаете с цепочками итераторов и не хотите создавать промежуточные векторы.
Сортировка с пользовательским ключом через sort_by_cached_key()
Если ключ сортировки вычисляется дорого (например, требует парсинга или обращения к БД), используйте sort_by_cached_key() — он вычисляет ключ один раз для каждого элемента и кэширует его:
let mut data = vec!["100", "20", "3", "1000"];
data.sort_by_cached_key(|s| s.parse::<i32>().unwrap());
println!("{:?}", data); // ["3", "20", "100", "1000"]
Этот метод доступен начиная с Rust 1.17 и значительно ускоряет сортировку, если ключ трудоёмкий.
Ответов пока нет.
Войдите чтобы оставить ответ
Блоги