Сортировка векторов в Rust: полный гайд для новичков с примерами

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Как отсортировать вектор в Rust: от новичка до профи

Представьте: вы пишете на Rust программу, которая собирает список пользователей — имена, возраст, рейтинг. Данные приходят в хаотичном порядке. Вам нужно вывести их по алфавиту или, скажем, отсортировать по возрасту. Знакомая задача? Если вы заглядывали на Stack Overflow, то наверняка видели вопрос «How to sort a vector in Rust?» — он собрал тысячи просмотров. И это не случайно.

Сортировка в Rust — одна из тех тем, где «просто вызвать sort()» может не сработать, если вы не знаете пары нюансов. В этой статье мы разберем все популярные способы сортировки вектора: от базового метода sort() до кастомных компараторов и выбора между стабильной и нестабильной сортировкой. Вы узнаете, как отсортировать вектор в Rust правильно, быстро и без паники.

Базовый метод: sort() — когда всё просто

Самый простой способ отсортировать вектор — использовать метод sort(). Он работает для типов, которые реализуют трейт Ord — а это все стандартные примитивы (числа, строки, символы).

fn main() {
    let mut numbers = vec![4, 2, 9, 1, 5, 6];
    numbers.sort();
    println!("{:?}", numbers); // Вывод: [1, 2, 4, 5, 6, 9]
}

Обратите внимание: переменная numbers объявлена как mut (mutable). Метод sort() изменяет вектор на месте, поэтому без mut код не скомпилируется. Это типичная ошибка новичков — забыть про изменяемость.

То же самое работает для строк:

fn main() {
    let mut fruits = vec!["banana", "apple", "cherry", "date"];
    fruits.sort();
    println!("{:?}", fruits); // Вывод: ["apple", "banana", "cherry", "date"]
}

Сортировка строк по умолчанию — лексикографическая (по алфавиту). Если вам нужна сортировка по длине или игнорируя регистр — читайте раздел про кастомные компараторы.

sort_unstable(): когда скорость важнее порядка

В Rust есть два варианта сортировки: sort() (стабильная) и sort_unstable() (нестабильная). Разница в том, что стабильная сортировка сохраняет относительный порядок равных элементов, а нестабильная — нет. Звучит академично, но на практике это важно, если вы сортируете сложные структуры и хотите сохранить порядок вставки для равных ключей.

Однако у sort_unstable() есть преимущество: она работает быстрее и использует меньше памяти. Для больших векторов (миллионы элементов) разница может быть существенной.

fn main() {
    let mut scores = vec![85, 92, 85, 70, 92, 60];
    scores.sort_unstable();
    println!("{:?}", scores); // Вывод: [60, 70, 85, 85, 92, 92]
    // Порядок двух 85 и двух 92 может отличаться от исходного
}

Когда выбирать sort_unstable? Если вам не важен порядок равных элементов и вы гонитесь за производительностью. Если же важна предсказуемость (например, сортировка таблицы с несколькими колонками), используйте sort().

sort_by(): кастомные компараторы

Что делать, если нужно отсортировать вектор по нестандартному правилу? Например, числа по убыванию, строки по длине, или пользовательские структуры по одному из полей. Для этого существует метод sort_by(), который принимает замыкание-компаратор.

Компаратор должен возвращать Ordering — один из вариантов: Less, Equal или Greater. Самый простой способ получить такой результат — использовать методы .cmp() или .partial_cmp().

Пример: сортировка чисел по убыванию.

fn main() {
    let mut numbers = vec![4, 2, 9, 1, 5];
    numbers.sort_by(|a, b| b.cmp(a));
    println!("{:?}", numbers); // Вывод: [9, 5, 4, 2, 1]
}

Обратите внимание: мы меняем местами a и b — b.cmp(a) даёт обратный порядок. Можно также использовать a.cmp(b).reverse().

Сортировка строк по длине:

fn main() {
    let mut words = vec!["rust", "python", "c", "java", "javascript"];
    words.sort_by(|a, b| a.len().cmp(&b.len()));
    println!("{:?}", words); // Вывод: ["c", "rust", "java", "python", "javascript"]
}

А вот пример с игнорированием регистра:

fn main() {
    let mut words = vec!["Apple", "banana", "Cherry", "date"];
    words.sort_by(|a, b| a.to_lowercase().cmp(&b.to_lowercase()));
    println!("{:?}", words); // Вывод: ["Apple", "banana", "Cherry", "date"]
}

Сортировка пользовательских структур

Чаще всего вам придётся сортировать не простые числа, а структуры данных. Допустим, у нас есть список пользователей, и мы хотим отсортировать их по возрасту.

#[derive(Debug)]
struct User {
    name: String,
    age: u32,
}

fn main() {
    let mut users = vec![
        User { name: "Alice".to_string(), age: 30 },
        User { name: "Bob".to_string(), age: 25 },
        User { name: "Charlie".to_string(), age: 35 },
    ];

    // Сортировка по возрасту (по возрастанию)
    users.sort_by(|a, b| a.age.cmp(&b.age));
    println!("{:?}", users);
    // Вывод: [User { name: "Bob", age: 25 }, User { name: "Alice", age: 30 }, User { name: "Charlie", age: 35 }]
}

Если вам нужно сортировать по нескольким полям (сначала по возрасту, потом по имени), можно сцепить вызовы cmp:

users.sort_by(|a, b| {
    a.age.cmp(&b.age)          // сначала по возрасту
        .then(a.name.cmp(&b.name)) // потом по имени
});

Метод then() работает как цепочка: если первый cmp вернул Equal, то вызывается второй. Очень удобно для многоключевой сортировки.

sort_by_key() и sort_unstable_by_key()

Если ваш компаратор просто извлекает ключ из элемента, можно использовать более лаконичные методы sort_by_key() и sort_unstable_by_key(). Они принимают замыкание, которое возвращает ключ, реализующий Ord.

Пример: сортировка пользователей по возрасту с помощью sort_by_key.

users.sort_by_key(|u| u.age);

Это эквивалентно предыдущему примеру с

Блоги

Рекомендаций по книгам