Rust 1.75 中的集合(Set):详细指南
Множества (HashSet) в Rust
Что такое множество и зачем оно нужно?
Представь, что у тебя есть коробка, в которую можно класть только уникальные предметы. Если ты попробуешь положить туда два одинаковых яблока, второе яблоко просто не поместится — оно уже есть! В программировании такая «коробка» называется множеством (Set).
Множество — это коллекция, которая хранит только уникальные элементы. В Rust для этого используется тип HashSet. Он идеально подходит, когда нужно:
- Убрать повторяющиеся элементы из списка
- Проверить, есть ли элемент в коллекции (очень быстро)
- Найти общие элементы между двумя списками
- Объединить два списка без дубликатов
Как создать и использовать множество
Чтобы использовать HashSet, его нужно сначала подключить в программе. Добавь в самое начало файла:
use std::collections::HashSet;
Теперь рассмотрим основные операции на примере простой программы:
use std::collections::HashSet;
fn main() {
// Создаём пустое множество
let mut fruits: HashSet<String> = HashSet::new();
// Добавляем элементы
fruits.insert(String::from("яблоко"));
fruits.insert(String::from("банан"));
fruits.insert(String::from("апельсин"));
fruits.insert(String::from("яблоко")); // Это не добавится!
println!("Фрукты в корзине: {:?}", fruits);
// Проверяем, есть ли элемент
if fruits.contains("банан") {
println!("Банан есть в корзине!");
}
// Удаляем элемент
fruits.remove("апельсин");
println!("После удаления: {:?}", fruits);
// Узнаём количество элементов
println!("Всего фруктов: {}", fruits.len());
}
Как превратить список в множество
Часто множества получают из обычных списков (векторов). Это делается с помощью метода into_iter().collect():
use std::collections::HashSet;
fn main() {
let mut input = String::new();
std::io::stdin().read_line(&mut input).unwrap();
// Разделяем строку по запятым и собираем в вектор
let items: Vec<&str> = input.trim().split(',').collect();
// Превращаем вектор в множество (дубликаты исчезнут)
let unique_items: HashSet<&str> = items.into_iter().collect();
println!("Уникальные элементы: {:?}", unique_items);
}
Разбор типичных задач
Задача 1: Ингредиенты пиццы
Пользователь вводит ингредиенты через запятую. Нужно оставить только уникальные.
use std::collections::HashSet;
use std::io;
fn main() {
println!("Введите ингредиенты через запятую:");
let mut input = String::new();
io::stdin().read_line(&mut input).unwrap();
// Убираем лишние пробелы и делим на части
let ingredients: Vec<&str> = input.trim()
.split(',')
.map(|s| s.trim()) // Убираем пробелы вокруг
.collect();
let unique: HashSet<&str> = ingredients.into_iter().collect();
println!("Уникальные ингредиенты для пиццы:");
for item in &unique {
println!("- {}", item);
}
}
Задача 2: Объединение списков покупок
Есть два списка покупок, нужно объединить их без повторов.
use std::collections::HashSet;
fn main() {
let list1 = vec!["молоко", "хлеб", "сыр", "яблоки"];
let list2 = vec!["сыр", "колбаса", "хлеб", "сок"];
// Превращаем оба списка в множества
let set1: HashSet<&str> = list1.into_iter().collect();
let set2: HashSet<&str> = list2.into_iter().collect();
// Объединяем (операция |)
let union: HashSet<&str> = set1.union(&set2).cloned().collect();
println!("Итоговый список покупок: {:?}", union);
}
Задача 3: Поиск общих товаров
Есть список обязательных товаров и список текущего наличия. Нужно найти, что уже есть.
use std::collections::HashSet;
fn main() {
let must_have = vec!["мука", "сахар", "соль", "масло", "яйца"];
let in_stock = vec!["сахар", "соль", "молоко"];
let must_set: HashSet<&str> = must_have.into_iter().collect();
let stock_set: HashSet<&str> = in_stock.into_iter().collect();
// Пересечение (операция &)
let available: HashSet<&str> = must_set.intersection(&stock_set).cloned().collect();
println!("Уже есть на складе: {:?}", available);
}
Частые ошибки новичков
- Забыть подключить HashSet: Всегда пиши
use std::collections::HashSet;в начале программы. - Пытаться получить элемент по индексу: В множестве нет индексов! Нельзя написать
set[0]. Используйcontainsдля проверки. - Путать с вектором: Вектор хранит элементы в порядке добавления и может содержать дубликаты. Множество не хранит порядок и не допускает дубликатов.
- Забыть про
mut: Если планируешь изменять множество (добавлять или удалять элементы), обязательно пишиlet mut set. - Сравнение строк: Rust чувствителен к регистру. "Яблоко" и "яблоко" — разные элементы.
Итоговый пример: Записная книжка контактов
Создадим программу, которая хранит имена контактов. Если пользователь пытается добавить уже существующее имя, программа сообщает об этом.
use std::collections::HashSet;
use std::io;
fn main() {
let mut contacts: HashSet<String> = HashSet::new();
loop {
println!("\n--- Записная книжка ---");
println!("1. Добавить контакт");
println!("2. Показать все контакты");
println!("3. Выйти");
println!("Выберите действие (1-3):");
let mut choice = String::new();
io::stdin().read_line(&mut choice).unwrap();
match choice.trim() {
"1" => {
println!("Введите имя контакта:");
let mut name = String::new();
io::stdin().read_line(&mut name).unwrap();
let name = name.trim().to_string();
if contacts.insert(name.clone()) {
println!("Контакт '{}' добавлен!", name);
} else {
println!("Контакт '{}' уже существует!", name);
}
}
"2" => {
if contacts.is_empty() {
println!("Записная книжка пуста.");
} else {
println!("Ваши контакты:");
for contact in &contacts {
println!("- {}", contact);
}
println!("Всего контактов: {}", contacts.len());
}
}
"3" => {
println!("До свидания!");
break;
}
_ => {
println!("Неверный выбор, попробуйте снова.");
}
}
}
}
Эта программа использует все основные возможности HashSet: добавление (с проверкой на уникальность), проверку пустоты, подсчёт элементов и перебор. Попробуй запустить её и добавить несколько контактов с одинаковыми именами — второе имя не добавится!