Rust для Java-разработчика: минимум
Чтобы написать плагин, весь Rust учить не нужно. На этой странице собрана та его часть, которая встречается в туториалах, рядом со знакомой вам Java, а в конце есть ссылки на случай, если захочется больше. Java-сниппеты здесь только иллюстрируют идею, в mcrs их нет. Rust-сниппеты тоже короткие наброски, и CI их не компилирует.
Нужный Rust в терминах Java
Заголовок раздела «Нужный Rust в терминах Java»| Rust | Идея из Java |
|---|---|
struct, enum |
class с полями, enum, который может нести данные |
Блок impl |
Методы класса |
trait |
interface |
Option<T> |
Значение, которое может быть null |
Result<T, E> и ? |
Проверяемые исключения (checked exceptions) |
match |
switch, обязанный покрыть все случаи |
Замыкание (closure) |x| ... |
Лямбда |
Vec, HashMap, String и &str |
List, Map, String |
#[derive(Clone)] |
Сгенерированный код копирования и equals |
&T и &mut T |
Много читателей или один писатель, проверяется при компиляции |
Модули, use, Cargo.toml |
Пакеты, импорты, build.gradle |
unsafe, процедурные макросы и async можно отложить на потом. Времена жизни (lifetime) тоже, хотя они будут попадаться вам в чужих сигнатурах.
Один и тот же код на Java и на Rust
Заголовок раздела «Один и тот же код на Java и на Rust»Класс из Java в Rust распадается на две части: struct с данными и блок impl с методами.
// класс с данными и методомclass Counter { private int ticks; void tick() { ticks++; }}// структура с данными и блок impl с методомstruct Counter { ticks: u32,}
impl Counter { fn tick(&mut self) { self.ticks += 1; }}Плагин для Paper наследует JavaPlugin, а плагин mcrs реализует трейт Plugin. Строка #[derive(Clone)] нужна потому, что каждое измерение получает свою копию плагина.
// плагин - реализация интерфейсаpublic class MyPlugin extends JavaPlugin { @Override public void onEnable() { /* регистрируем */ }}// плагин - реализация трейта; для измерений он должен быть Clone#[derive(Clone)]struct MyPlugin;
impl Plugin for MyPlugin { fn build(&self, app: &mut App) { // регистрируем системы, ресурсы, наблюдатели }}Там, где Java возвращает null или бросает исключение, Rust возвращает Option или Result, поэтому оба случая видны в типе. Оператор ? отдает ошибку вызывающему коду.
// null значит "нет значения", ошибки - исключенияPlayer p = lookup(name);if (p == null) return;int n = Integer.parseInt(text); // может бросить исключение// Option и Result делают оба случая видимыми в типеlet Some(p) = lookup(name) else { return };let n: i32 = text.parse()?; // отдает ошибку вызывающемуЗамыкание выполняет ту же работу, что и лямбда. Во вкладке Rust замыкание - это наблюдатель (observer), то есть функция, которая срабатывает при событии.
// лямбда как обработчикevents.on(ChatEvent.class, e -> e.setCancelled(true));// замыкание как обработчик (наблюдатель)app.add_observer(|mut e: On<PlayerChatEvent>| { e.cancelled = true;});Следующий пример - настоящий плагин из репозитория. В нем есть ресурс и система, которая работает в FixedUpdate.
#[derive(Resource, Default)]struct Ticks<const ALL: bool>(u32);
#[derive(Message)]struct Ticked;
#[derive(Clone)]struct CountTicks<const ALL: bool>;
impl<const ALL: bool> Plugin for CountTicks<ALL> { fn name(&self) -> &str { if ALL { "count every tick" } else { "count overworld ticks" } }
fn build(&self, app: &mut App) { app.init_resource::<Ticks<ALL>>().add_systems( FixedUpdate, |mut ticks: ResMut<Ticks<ALL>>, mut ticked: MessageWriter<Ticked>| { ticks.0 += 1; if ALL { ticked.write(Ticked); } }, ); app.add_message::<Ticked>(); }}crates/mcrs_minecraft_server/tests/it/dimension_plugins.rs:10-40
У значения один владелец
Заголовок раздела «У значения один владелец»У значения в Rust один владелец. Владелец может одолжить значение как &T многим читателям или как &mut T одному писателю, но не обоими способами сразу. В системе Bevy это записано в параметрах: Query<&T> читает, Query<&mut T> пишет. Времена жизни приходится писать редко.
Из-за этого меняются две привычки. Там, где вы хранили бы ссылку на игрока, храните id Entity, а там, где делились бы значением, вызывайте .clone(). Не копируйте паттерны Rust-серверов, построенных на Arc<Mutex<..>>, потому что проект избегает их в библиотечных слоях.1 Правила целиком объясняет глава Rust Book о владении.
Три ошибки, которые вам встретятся
Заголовок раздела «Три ошибки, которые вам встретятся»| Ошибка | Причина | Что делать |
|---|---|---|
| Использование перемещенного значения | Вы передали значение по значению, а потом использовали его снова | Передайте &value или вызовите .clone() |
| Функция не принимается как система | У одного из параметров тип, который Bevy не умеет подставлять | Проверьте, что каждый параметр - это Res, ResMut, Query, Commands, читатель или писатель сообщений либо Local |
Два параметра одной системы требуют одни и те же данные, и один из них как &mut |
Планировщик сообщает об этом на старте, когда строит граф, а компилятор такое не ловит | Объедините запросы или разделите систему |
Что читать дальше
Заголовок раздела «Что читать дальше»- В книге The Rust Programming Language прочитайте главы про владение, enum и
match, обработку ошибок и трейты. - В Rust by Example собраны короткие запускаемые примеры.
- Rustlings - набор небольших упражнений.
- Руководство Cargo рассказывает про
Cargo.tomlи зависимости. - Bevy learn и примеры ECS знакомят с Bevy.
- Unofficial Bevy Cheat Book может отставать. В mcrs используется Bevy 0.19, где типы называются
Message,MessageReaderиOn<E>, а в старых текстах названия могут быть другими. - На docs.rs для bevy_ecs есть точные сигнатуры.
Дальше: первый плагин, затем Paper и Minestom в терминах mcrs.
Footnotes
Заголовок раздела «Footnotes»Источники и проверка
Проверено: 26.4-snapshot-7, 51d45c26a, 2026-11-09
Переведено с версии 51d45c26a
- crates/mcrs_minecraft_server/tests/it/dimension_plugins.rs
- crates/mcrs_minecraft_engine_world/src/world/sub_app.rs
- rust-toolchain.toml
- issue #321
Следить: #321