extern crate vs use в Rust: как не путать и писать правильно

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

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

Начать курс

extern crate и use в Rust: в чём подвох и как не наступать на грабли

Если вы когда-нибудь писали на Rust хотя бы пару строк, то наверняка сталкивались с конструкциями extern crate и use. На первый взгляд кажется, что они делают одно и то же — подключают внешний код. Но дьявол, как всегда, кроется в деталях. Этот вопрос входит в топ самых популярных на Stack Overflow (тысячи просмотров!), и новички путаются в нём постоянно. Давайте раз и навсегда разберёмся, чем отличается extern crate от use, где какая магия происходит и как избежать типичных ошибок.

Коротко о главном: что есть что

Представьте, что ваш проект на Rust — это большой дом. extern crate — это доставка стройматериалов на участок. Вы говорите компилятору: «Эй, вот этот ящик с кирпичами (крейт) я хочу использовать, привези его». Без этой команды компилятор даже не будет знать о существовании внешней библиотеки.

use — это уже внутренняя навигация по дому. Вы говорите: «Я хочу, чтобы дверь из этой комнаты (модуля) вела прямо на кухню (конкретный тип или функцию)». use создаёт алиасы (короткие пути) к уже известным модулям и типам, чтобы не писать каждый раз полный путь.

Проще говоря: extern crate отвечает на вопрос «что мы вообще можем использовать?», а use — на вопрос «как нам удобнее к этому обращаться?».

Исторический контекст: почему все путаются

Путаница возникла не на пустом месте. В старых версиях Rust (до 2018 edition) extern crate был обязателен для любого внешнего крейта. Выглядело это так:

// Rust 2015 — старый стиль
extern crate serde;
use serde::{Serialize, Deserialize};

Начиная с Rust 2018 edition, компилятор научился сам находить зависимости из Cargo.toml. Теперь писать extern crate для большинства крейтов не нужно. Достаточно просто добавить зависимость в Cargo.toml и сразу использовать use:

// Rust 2018+ — новый стиль (extern crate не обязателен)
use serde::{Serialize, Deserialize};

Вот тут-то и начинается ад для новичков. Они видят старые примеры, где extern crate используется, новые, где его нет, и пытаются понять, когда он всё-таки нужен. Спойлер: нужен, но в очень редких случаях.

Когда extern crate всё ещё нужен (и это важно!)

Есть три ситуации, где без extern crate не обойтись:

  • Связывание с системными библиотеками через #[link] — если вы пишете биндинги к C/C++ коду и используете атрибут #[link(name = "sqlite3")], то extern crate обязателен.
  • Глобальные макросы — некоторые крейты (например, log) экспортируют макросы, которые должны быть видны во всей программе. В Rust 2018 для этого нужно писать extern crate log;.
  • Сборка с нестандартными именами крейтов — если вы используете extern crate my_crate as foo;, чтобы переименовать крейт.

Во всех остальных случаях extern crate — это артефакт прошлого. Но многие разработчики по привычке продолжают его писать, а новички копируют код из старых туториалов.

Топ-5 ошибок при работе с extern crate и use

Ошибка №1: Использование extern crate там, где он не нужен

Самая частая проблема. Вы добавляете крейт в Cargo.toml, потом пишете extern crate rand; — и код работает. Но это лишняя строчка. В современных проектах (Rust 2018+) она только засоряет код и может сбить с толку других разработчиков.

// Плохо (избыточно в Rust 2018+)
extern crate rand;
use rand::Rng;

// Хорошо (достаточно)
use rand::Rng;

Ошибка №2: Попытка использовать use без extern crate для системных библиотек

Обратная ситуация. Вы пишете:

use std::os::raw::c_int; // Это работает, потому что std — встроенный крейт

Но если вы работаете с внешней C-библиотекой через #[link], то без extern crate ничего не заработает:

// Не скомпилируется без extern crate
use my_sys_bindings::some_function; // Ошибка: unresolved import

// Правильно:
extern crate my_sys_bindings;
use my_sys_bindings::some_function;

Ошибка №3: Путаница с путями модулей

Многие думают, что use может импортировать что угодно откуда угодно. На самом деле use работает только с тем, что уже «видит» компилятор. Вы не можете сделать use some_crate::foo;, если крейт some_crate не объявлен в Cargo.toml или через extern crate.

// Cargo.toml
// [dependencies]
// serde = "1.0"

// main.rs — работает
use serde::Serialize;

// main.rs — НЕ работает, если нет extern crate или записи в Cargo.toml
use non_existent_crate::Foo; // Ошибка: unresolved import

Ошибка №4: Забытый атрибут crate_type

Если вы пишете библиотеку (lib.rs), а не исполняемый файл, Rust автоматически делает все публичные элементы доступными. Но если вы пишете биндинги или используете extern crate для системных библиотек, иногда нужно указать #![crate_type = "lib"]. Без этого компилятор может не понять, как линковать крейт.

Ошибка №5: Использование extern crate внутри модуля

extern crate — это глобальная директива. Её место в корне файла (main.rs или lib.rs). Если вы напишете её внутри вложенного модуля, компилятор выдаст предупреждение или ошибку:

// main.rs
mod my_module {
    extern crate serde; // Ошибка! extern crate должен быть на уровне крейта
}

Правильно выносить extern crate на верхний уровень, а use — уже внутри модулей по необходимости.

Практический пример: от старого кода к новому

Блоги

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