Avatar ·

Как отсортировать вектор в Rust: полное руководство с примерами

Всем привет! Я только начал изучать Rust и столкнулся с проблемой сортировки вектора. В других языках, вроде Python или JavaScript, это делается одной строкой, а тут я совсем запутался. У меня есть вектор чисел: ```rust let mut numbers = vec![5, 2, 8, 1, 9, 3]; ``` Я хочу отсортировать его по возрастанию. Пытался сделать так: ```rust numbers.sort(); ``` Но компилятор выдаёт ошибку: "the trait `std::cmp::Ord` is not implemented for `i32`". Хотя я думал, что для чисел это должно работать из коробки. Также у меня есть вектор строк: ```rust let mut fruits = vec!["banana", "apple", "cherry", "date"]; ``` Тут сортировка вообще не работает — компилятор пишет про заимствование и перемещение. Ещё я пробовал сортировать структуры: ```rust 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 }, ]; // Как отсортировать по возрасту? ``` Подскажите, пожалуйста, как правильно сортировать векторы в Rust? Мне нужно понять: 1. Как сортировать числа и строки по возрастанию/убыванию? 2. Как сортировать пользовательские структуры? 3. В чём разница между sort() и sort_unstable()? 4. Можно ли сортировать с кастомным компаратором? Заранее спасибо за помощь! <<>>

Основы сортировки векторов в 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 и значительно ускоряет сортировку, если ключ трудоёмкий.