Rust: пишем один код для Windows, macOS и Linux без боли

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

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

Начать курс

Почему Rust — идеальный кандидат для кроссплатформенной разработки?

В мире разработки ПО есть вечная боль: написать программу один раз и запустить её на Windows, macOS и Linux. Обычно это превращается в ад с условными директивами, танцами с бубном вокруг путей к файлам и бесконечным тестированием. Но есть язык, который решает эту проблему на системном уровне — Rust.

Rust был спроектирован с прицелом на кроссплатформенность. Его компилятор (rustc) умеет генерировать бинарники для десятков архитектур и ОС. Но главное — в стандартной библиотеке (std) уже заложена абстракция над операционной системой. Вы пишете код, а Rust сам разбирается с тем, как работать с файлами, потоками или сетью на конкретной платформе. Это не магия, это грамотный дизайн.

В этой статье мы не просто поговорим о теории. Мы разберём реальные примеры кода: от простого «Hello, World!» до работы с файловой системой и создания GUI-приложения. Вы увидите, как кроссплатформенная разработка на Rust превращается из головной боли в удовольствие. Поехали!

Как Rust обеспечивает кроссплатформенность?

Секрет Rust в его трехуровневой системе абстракции:

  • Платформонезависимый код — 90% вашей программы. Использует только std, работает везде одинаково.
  • Условная компиляция — для платформо-зависимых кусков. Rust позволяет включать или выключать код в зависимости от целевой ОС.
  • Внешние крейты (crates) — библиотеки вроде serde или tokio, которые уже решают кроссплатформенные проблемы за вас.

Базовый принцип: вы пишете общий код, а для редких исключений используете директивы #[cfg(target_os = "...")]. Это чище, чем в C/C++, где часто приходится городить целые блоки #ifdef.

Пример: определяем платформу в коде

Посмотрим, как узнать, под какой ОС запущена программа, без грязных хаков:

fn main() {
    if cfg!(target_os = "windows") {
        println!("Запущено на Windows");
    } else if cfg!(target_os = "macos") {
        println!("Запущено на macOS");
    } else if cfg!(target_os = "linux") {
        println!("Запущено на Linux");
    } else {
        println!("Неизвестная ОС (но Rust всё равно работает!)");
    }
}

Важно: cfg! — это макрос, который вычисляется на этапе компиляции. Если вы скомпилируете этот код под Linux, то в бинарник попадёт только ветка с Linux. Нет лишнего кода — нет проблем.

Работа с файловой системой: пишем один раз

Классическая головная боль — пути к файлам. На Windows это C:\Users\name\file.txt, на Linux — /home/name/file.txt. Rust решает это через типаж Path и структуру PathBuf.

Рассмотрим пример: создание директории и запись в файл. Код будет работать на любой ОС без изменений:

use std::fs;
use std::path::PathBuf;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Строим путь к домашней директории пользователя
    let home = dirs::home_dir().expect("Не удалось получить домашнюю директорию");
    let mut path = PathBuf::from(home);
    path.push("my_rust_app");
    path.push("data");
    
    // Создаём директорию (рекурсивно)
    fs::create_dir_all(&path)?;
    
    // Создаём файл
    let file_path = path.join("output.txt");
    fs::write(&file_path, "Hello, кроссплатформенный мир!")?;
    
    println!("Файл сохранён: {:?}", file_path);
    Ok(())
}

Здесь мы использовали крейт dirs, который даёт кроссплатформенный доступ к системным путям (домашняя папка, AppData и т.д.). Без него пришлось бы писать условную компиляцию для каждой ОС.

Сеть и работа с потоками

Стандартная библиотека Rust предоставляет единый API для работы с TCP/UDP, HTTP и потоками. Например, простой TCP-сервер будет выглядеть одинаково на всех платформах:

use std::io::{Read, Write};
use std::net::{TcpListener, TcpStream};
use std::thread;

fn handle_client(mut stream: TcpStream) {
    let mut buffer = [0; 1024];
    stream.read(&mut buffer).unwrap();
    stream.write(b"Привет от Rust!").unwrap();
}

fn main() {
    let listener = TcpListener::bind("127.0.0.1:8080").unwrap();
    println!("Сервер запущен на localhost:8080");
    
    for stream in listener.incoming() {
        match stream {
            Ok(stream) => {
                thread::spawn(|| handle_client(stream));
            }
            Err(e) => eprintln!("Ошибка: {}", e),
        }
    }
}

Этот код скомпилируется и запустится на Windows, macOS и Linux без единого изменения. Rust берёт на себя работу с сокетами и потоками ОС.

GUI на Rust: кроссплатформенность в действии

Многие думают, что кроссплатформенный GUI — это сложно. Но Rust меняет правила игры. Рассмотрим два популярных подхода.

1. Tauri — десктопные приложения на HTML/CSS/JS + Rust

Tauri позволяет писать бэкенд на Rust, а интерфейс — на веб-технологиях. Приложение компилируется в нативный бинарник под каждую ОС.

// Бэкенд на Rust
#[tauri::command]
fn greet(name: &str) -> String {
    format!("Привет, {}! Ты используешь Rust.", name)
}

fn main() {
    tauri::Builder::default()
        .invoke_handler(tauri::generate_handler![greet])
        .run(tauri::generate_context!())
        .expect("Ошибка запуска");
}

Фронтенд — обычный HTML с JavaScript. Tauri упаковывает всё в один исполняемый файл. Размер приложения — около 3 МБ, а не 100+ МБ, как у Electron.

2. egui — чистый Rust GUI

Если вы хотите писать интерфейс полностью на Rust, обратите внимание на крейт egui. Он рендерит интерфейс через OpenGL/Vulkan/Metal и работает везде.

use eframe::egui;

fn main() {
    let options = eframe::NativeOptions::default();
    eframe::run_native(
        "Кроссплатформенное приложение",
        options,
        Box::new(|_cc| Box::new(MyApp::default())),
    );
}

struct MyApp {
    name: String,
}

impl Default for MyApp {
    fn default() -> Self {
        Self { name: String::new() }
    }
}

impl eframe::App for MyApp {
    fn update(&                

Блоги

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