В чем разница между use и extern crate в Rust? Когда что использовать?
Краткий ответ: extern crate подключает крейт, use импортирует из него имена
Вы столкнулись с классической путаницей новичков. Давайте разберем по порядку. В Rust система импорта состоит из двух уровней:
- Подключение крейта — сделать внешнюю библиотеку доступной для вашего проекта.
- Импорт имен — вытащить конкретные типы, функции или трейты из подключенного крейта в текущую область видимости.
extern crate отвечает за первый уровень. Он говорит компилятору: "Эй, найди крейт с таким именем среди зависимостей (из Cargo.toml) и сделай его доступным как модуль верхнего уровня". Без этой команды Rust просто не знает, что такое rand в коде.
use отвечает за второй уровень. Он создает короткие алиасы для путей к элементам внутри уже подключенного крейта. Например:
extern crate rand; // Подключаем крейт
// Теперь rand::Rng — это валидный путь, но писать его каждый раз длинно
use rand::Rng; // Импортируем трейт Rng в текущую область видимости
fn main() {
let mut rng = rand::thread_rng(); // rand::thread_rng — это функция из крейта
let n: u32 = rng.gen(); // gen() — метод трейта Rng
}
Почему в современных проектах extern crate не пишут?
Начиная с Rust 2018 edition, extern crate стал опциональным для большинства случаев. Если крейт указан в Cargo.toml, компилятор автоматически подключает его как модуль верхнего уровня. То есть современный эквивалент предыдущего примера:
// Cargo.toml: rand = "0.8"
// В коде extern crate rand; не нужен!
use rand::Rng; // Работает напрямую
fn main() {
let mut rng = rand::thread_rng();
let n: u32 = rng.gen();
}
Однако есть случаи, когда extern crate все еще обязателен:
- Макиросы: если крейт экспортирует макросы (
#[macro_use] extern crate serde;) - Крейты без модульной системы: некоторые старые крейты требуют явного подключения
- Переименование крейта:
extern crate foo as bar; - Пользовательские атрибуты:
extern crate proc_macro;
Правило большого пальца
Если вы используете Rust 2018+ (а это стандарт с 2020 года):
- Добавьте зависимость в
Cargo.toml - Используйте
useдля импорта нужных имен - Забудьте про
extern crate, пока не столкнетесь с одним из исключений выше
Ваш второй пример не работал, потому что вы, вероятно, использовали старый edition (2015) или не указали edition = "2018" в Cargo.toml. Проверьте это!
// Cargo.toml
[package]
name = "my_project"
version = "0.1.0"
edition = "2021" // или 2018
[dependencies]
rand = "0.8"
После этого use rand::Rng; будет работать без extern crate.
Дополнение: use для локальных модулей и иерархия импорта
Важно понимать, что use работает не только с внешними крейтами, но и с вашими собственными модулями. Это универсальный инструмент для навигации по модульной системе Rust.
// src/main.rs
mod utils; // объявляем модуль
// Без use:
fn main() {
utils::helper::do_something(); // полный путь
}
// С use:
use utils::helper;
fn main() {
helper::do_something(); // короче
}
А extern crate — это исключительно про внешние зависимости. Его можно представить как "регистрацию" внешнего пакета в вашем проекте. После регистрации вы можете обращаться к нему через :: (как к модулю) и использовать use для сокращения путей.
Еще один нюанс: use может импортировать не только модули, но и отдельные функции, типы и трейты:
use std::collections::HashMap; // тип
use std::fs::File; // тип
use std::io::Read; // трейт
use rand::random; // функция
fn main() {
let mut map = HashMap::new();
map.insert("key", random::<u32>());
}
Если вы когда-нибудь увидите extern crate в современном коде (Rust 2018+), скорее всего это либо:
- Наследие (код не обновляли)
- Специфический случай с макросами
- Плагин компилятора (например,
extern crate proc_macro;)
В обычной разработке достаточно Cargo.toml + use.