После работы над двумя коммерческими проектами на Rust я получил хороший практический опыт в этом языке. Это были backend сервисы для веб-приложений, где Rust использовался для основной бизнес-логики и работы с базами данных. Кроме того, я создал три open source библиотеки на Rust, которые публиковал на GitHub. Это позволило мне лучше изучить идиоматичный Rust, работу с асинхронностью и т. д. В целом, после работы над этими проектами у меня как Java-разработчика накопился интересный опыт, которым хотелось бы поделиться для тех, кто только начинает изучать Rust, приходя из мира Java. Далее я привожу несколько полезных советов, которые помогут в переходе на Rust. В Rust есть два типа строк - &str и String. Первый представляет собой ссылку на строку, второй - владеющую строку. На первый взгляд кажется, что &str - это аналог String из Java, который лучше использовать по умолчанию. Однако на практике проще начинать с String. Этот тип проще использовать, он ведет себя схожим с привычной строкой в Java. А &str требует следить за владением и ссылками. Поэтому я рекомендую в первой версии кода использовать String, а затем оптимизировать узкие места с помощью &str. В Java у нас есть инструменты для отладки, и в Rust тоже есть свои. Однако, чтобы облегчить процесс отладки, вы можете использовать атрибут #[derive(Debug)] на структурах, которые хотите анализировать. Это позволит вам автоматически генерировать реализацию метода fmt::Debug, который можно использовать для вывода состояния объекта при помощи функции println!. В Rust нет сборки мусора, поэтому важно явно управлять памятью. Часто рекомендуют использовать ссылки вида &T для передачи данных в функции. Но на первых порах лучше этого избегать. Вместо этого создавайте копии объектов с помощью метода clone(): Это позволит избежать многих типичных ошибок. Потом, когда код заработает, можно оптимизировать узкие места, перейдя на ссылки. В Java принято создавать классы для организации кода, а Rust использует структуры (struct) для той же цели. Вместо того чтобы определять методы внутри структур, как это делается в классах, мы определяем функции и реализуем их для конкретных структур. Вместо конструктора реализуют функцию new. Для асинхронного кода вернуть лучше Arc<MyClass>, а для синхронного - Rc<MyClass>. Это позволит имитировать семантику ссылок на объекты в Java. В Java ошибки обрабатываются с помощью исключений. В Rust исключений нет. Вместо них используется тип Result<T, E>, который представляет собой пару: значение T и ошибка E. Вот пример функции в Java, которая может вернуть значение или ошибку: Эта функция может вернуть ошибку IOException, если файл не существует. Чтобы обработать эту ошибку, мы можем использовать try-catch. В Rust мы можем написать функцию, используя тип Result<T, E>: В Java, чтобы эскалировать ошибку по коду выше, мы можем использовать оператор throw или в определении функции просто указать тип исключения, который там может произойти. В Rust для этого используется оператор ?. Вместо всеми любимого unwrap(), который может вызвать панику в случае ошибки, мы можем использовать оператор ?, чтобы просто передать ошибку выше. Это позволяет избежать паник и дает контроль над обработкой ошибок на более высоком уровне. В этом примере, если файл не существует, функция вернет ошибку типа Error. Эта ошибка будет передана вызывающему коду. В Java для объявления ошибок используется Exception. В Rust такого нет. Вместо этого ошибки объявляются с помощью типов. Использование библиотеки thiserror делает объявление ошибок в Rust очень удобным и простым. С помощью этой библиотеки вы можете использовать макросы для объявления ошибок, которые автоматически генерируют код для их реализации.  Использование библиотеки thiserror также позволяет вам автоматически конвертировать ошибки сторонней библиотеки в вашу ошибку. В Rust я также рекомендую избегать непосредственного использования println! для логирования. Вместо этого я подключаю библиотеку log и использую макросы вроде info!, warn! или debug!. А затем в main инициализировать env_logger, который позволяет легко настроить вывод логов в stdout или файл. Этот подход делает код намного чище и позволяет гибко управлять логированием приложения. На Java я часто использовал именно такой паттерн, поэтому мне было приятно найти похожее решение в экосистеме Rust. В Java, когда вы хотите, чтобы переменная была доступна в нескольких потоках для чтения вы просто передаете ссылку. В Rust, аналогом этих структур является Arc. Arc позволяет безопасно передавать ссылку на переменную между потоками, но не позволяет изменять эту переменную. В Java, когда вы хотите, чтобы переменная была доступна в нескольких потоках для модификации, вы используете AtomicReference или ConcurrentHashMap. Если вы хотите в Rust, чтобы переменная была доступна в нескольких потоках и её можно было изменять, вам нужно использовать Mutex. Mutex позволяет получить эксклюзивную блокировку переменной, что позволяет другим потокам ждать, пока поток получивший доступ через блокировку не освободит ее. Кстати лучше сразу использовать futures::lock::Mutex, а не std::sync::Mutex, так как второй спроектирован для не асинхронного кода. Вот пример того, как использовать Mutex для асинхронного кода: В этом примере мы создаём переменную counter типа Mutex. Затем мы запускаем два асинхронных потока, которые увеличивают значение счетчика на единицу. В конце программы мы печатаем значение счетчика, которое должно быть равно 2. Еще одним интересным аспектом перехода с Java на Rust является использование трейтов (traits). В Rust нельзя объявить асинхронные функции внутри трейтов напрямую. Однако существует библиотека под названием "async_trait", которая позволяет обойти это ограничение. Благодаря библиотеке "async_trait" мы можем использовать асинхронные функции внутри трейтов. При переходе с Java на Rust лучше начинать с простых типов вроде String, избегая сложных вроде &str. Это позволит быстрее написать рабочую версию кода. Использование структур и ассоциированных функций вместо классов соответствует идиоматичному Rust. Для обработки ошибок в Rust используется тип Result вместо исключений как в Java. Библиотеки вроде thiserror, log и async_trait упрощают написание idiomatic кода на Rust. Переход с Java на Rust может показаться непростым из-за отличий в подходах этих языков. Главное - начать с простых конструкций, затем постепенно переходить к идиоматичному коду.   Rust Developer