Перейти к содержимому

Rust для Java-разработчика: минимум

Меняется. Следите: #321

Чтобы написать плагин, весь Rust учить не нужно. На этой странице собрана та его часть, которая встречается в туториалах, рядом со знакомой вам Java, а в конце есть ссылки на случай, если захочется больше. Java-сниппеты здесь только иллюстрируют идею, в mcrs их нет. Rust-сниппеты тоже короткие наброски, и CI их не компилирует.

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 распадается на две части: struct с данными и блок impl с методами.

// класс с данными и методом
class Counter {
private int ticks;
void tick() { ticks++; }
}

Плагин для Paper наследует JavaPlugin, а плагин mcrs реализует трейт Plugin. Строка #[derive(Clone)] нужна потому, что каждое измерение получает свою копию плагина.

// плагин - реализация интерфейса
public class MyPlugin extends JavaPlugin {
@Override
public void onEnable() { /* регистрируем */ }
}

Там, где Java возвращает null или бросает исключение, Rust возвращает Option или Result, поэтому оба случая видны в типе. Оператор ? отдает ошибку вызывающему коду.

// null значит "нет значения", ошибки - исключения
Player p = lookup(name);
if (p == null) return;
int n = Integer.parseInt(text); // может бросить исключение

Замыкание выполняет ту же работу, что и лямбда. Во вкладке Rust замыкание - это наблюдатель (observer), то есть функция, которая срабатывает при событии.

// лямбда как обработчик
events.on(ChatEvent.class, e -> e.setCancelled(true));

Следующий пример - настоящий плагин из репозитория. В нем есть ресурс и система, которая работает в FixedUpdate.

CountTicks: плагин с ресурсом и системой
#[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.

  1. В работе#566 Направление: библиотечный код без общего владения, блокировок, трейт-объектов и аллокаций на горячем пути. ↩

  2. Решения нет#321 Отслеживает подготовку крейтов протокола, сети и игровых данных к публикации. ↩

Источники и проверка

Проверено: 26.4-snapshot-7, 51d45c26a, 2026-11-09

Переведено с версии 51d45c26a

Следить: #321