Перевод программы с Go на Rust

Мы переписали один существующий проект, изначально написанную на Go, на язык Rust. Использование новой платформы позволит нам достичь большего ускорения выполнения программного кода, а также использовать возможности, присущие определенным языкам для борьбы с проблемами вроде гонок.

Ваш проект состоит из нескольких частей:

  1. Основная структура приложения main.go
  2. Загрузка настроек config.yml или переменных окружения
  3. Подключение к PostgreSQL и SQLite
  4. Обработка публикаций МКТТ (mqtt)
  5. Синхронизация данных между базами данных (sync_loop)

Далее мы рассмотрим важной из частей кода, которые были переписаны: обработка MQTT-сообщений и работа в синхронном цикле.

Пример кадовой работы на Rust

Изменим наш основной модуль, добавим необходимые библиотеки для подключения:

RUST
 
use crate::mqtt;
use crate::sync;

#[tokio_main]
async fn main() {
    /* Открываем два потока: один отвечает за MQTT, другой за синхронизацию */
    let mqtt_handle = tokio::spawn(mqtt::mqtt_loop());
    let sync_handle = tokio_aspawn(sync::sync_loop());

    // Синхронизируем запуск
    drop((mqtt_handle.await, sync_handle.await));

    /* Возвращаемся к основной функции если есть какая-либо задача после того, как оба тела потоку отключатся. Например: вывід завершению, логирование ошибок и так далее.
     */
}

В коде выше mqtt_loop() ищет определенное социальное сообщение МКТТ и затем сохраняет это в локальной базе данных.

Важные изменения между Go и Rust

  1. Перключение к PostgreSQL: В Go использовался sql.Open() с инициализацией параметров подключения, во время как в Rust через Diesel подключаемся вызвав функцию establish_connection(). Это позволяет более гибко обрабатывать ошибки при подключении.

  2. Обработка ошибок: В Go используется систематическое закрытие всех открытых ресурсов с помощью оператора defer перед каждым вызовом, что предотвращает утечка памяти и блокировку файлового дексриптора. Rust реализация использует соответствующие модели типизации и обертки над системными вызовами.

Преобразование базового MQTT-loop

В код для приложения MCKT представлен следующим образом на Rust:

RUST
 
use crate::settings::CONFIG;
use crate::sqlite::{InsertSensorData, establish_local_connection};
use diesel::RunQueryDsl;
use rumqttc::{Client, QoS};

pub async fn mqtt_loop() -> Result<(), ConnectionError> {
    /* Эта часть кода идентична. Происходят инициализации клиента, события подписки и слушка соединений */
...

Замечается, что Rust изначально включает в себя синхронизацию данных на основе системных мутексов, обеспечивая безопасность асинхронного кода. Тем не менее из-за простоты нашего использования MQTT (подписка для одной публикиции), то можно сказать, что сложностей с нитями и трулами возникающих в Rust для этого конкретной задачи пока нет особого количества.

Хоть обновлённый подход на языке Rust добавителен в объему работы, мы в принципе не используем столько "плюшек" от асинхронного программирования.

Особенности ментальитета кодера после переключения

Достижении улучщённой производительности:

Сравнивая приложение Rust (нампирайте на самом деле пять) с его исходным Go-аналогом, можно наблюдать выдающийся размер файла бинаря изначального кода, 3.2Mb против только 0.8Mb для программы, написанной на Rust.

Эффективность мемоции:

Rust использует модели типизации для безопасного контроля памяти и ужём размер программки, что в свою очередь значительно влияет по производительности запуска и выполнения бинарника.

Асинхронное общение:

Rus тает при разработке с различными потоками изначально обрабатывает синхронность, так выдерживает параллельные задачи, что отражается в большей под нагрузкой и быстроты кода в сравнении со своим Go аналогом.

Однако Rust также требует времени присвоения новой структуры переменным и типам данных, поэтому важно следить за их управление, и это можно считать не совсем эффективно.

Пример: Структуры данных (schema.rs)

На основе данных из базы, данные строятся по определенной схеме в PostgreSQL (schema.rs):

Go

GO
 
var schema = `CREATE TABLE IF NOT EXISTS sensor_data(
    id SERIAL PRIMARY KEY,
    gateway_id INT8 UNIQUE NOT NULL,
    device MACADDR8,
    rssi SMALLINT NOT NULL,
    datetime INT8,
    Sensor VARCHAR(36)
)`

Все улучшения в использовании данных происходят через приведения на Rust с помощью Diesel ORM:

RUST
 
table! {
    sensor_data(id) {
        id -> BigInt,
        gateway_id -> BigInt,
        device -> Text,
        rssi -> Integer,
        datetime -> Integer,
        sensor -> Text,
    }
}

Преимущества языка Rust:

  1. Уменьшение размер бинаря: Исходный язык Go в среднем приблизился к около трети размерам программы написаной на Rust, что снижено вдвое только без оптимизации конфигурации билдером.

  2. Безопасность памяти и управление ей по сравненению с Go: Несмотря на то, что оба языка являются безопасными для памяти, использование Rust значительно снижает количество логических ошибок в коде, связанных с неправильной обработкой памяти через структуры и типы данных.

  3. Интеграция библиотеки Diesel: Код организации запросов базы данных Rust и использование ORM делает чтение и поддержку кода заметно легче по сравнению с низкоуровневыми SQL-вызовами Go.

  4. Улучшенное управление языком асинхронного кода из Golang: Вместо того, чтобы переключаться между различными системами обработки (Goroutines и Channels vs. Futures and Aysn/Await), язык Rust автоматически гарантирует безопасную синхронизацию данных, что предотвращает гонки в нитях за данными.

  5. Можете проверить это для кода MQTT поддерживает библиотеку Diesel и управление событиями в асинхронных циклах (сборщик мусора).

Так что для использования Rust как перспективного языка, которые следует заменению Go, с учетом его сложной моделью типизации, безопасных нитей и автоматического управления памятью, а также меньшего объема бинарник в прикладке.

В конце-концов при выполнении наших требований в нашем проекте был внедрён лучшим образом языок программирования Rust.

При разработке коды на нашем сайтах вы сможете оптимизировать и улучшить существование данных. Каждое разворачивание, используя преимущества особенностей этого нового языка, добавит вам индивидуального тонкого контроля над программами.