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

GUI на Windows — выбор стека

Разработчику Архитектору

GUI на Windows — выбор стека

О чём эта глава

Если у проекта в Rust появляется требование "нужно полноценное окно на рабочем столе Windows", быстро встаёт вопрос выбора стека. Вариантов много, и у каждого свой компромисс по скорости разработки, интеграции с ОС и поддержке.

GUI (Graphical User Interface) — окна, кнопки, поля ввода, меню. На Windows интерфейс можно строить по-разному: через системные API Microsoft, через кроссплатформенные библиотеки на Rust или через гибрид (логика на Rust, картинка в WebView).

Цель статьи — помочь выбрать стек и понять логику выбора. Для старта в Rust разумнее первая программа и при необходимости веб-API на Axum; GUI подключают, когда продукту действительно нужен desktop-интерфейс.

Обзор: приложения на Rust · фреймворки · нативный код: FFI.


Базовые термины

ТерминПростыми словами
Нативный UIРисует система Windows (кнопки выглядят как в других программах ОС).
Собственный рендерФреймворк сам рисует интерфейс (OpenGL, WGPU, свой движок).
WebViewВ окне встроен мини-браузер (HTML/CSS/JS), Rust — "бэкенд".
HWNDИдентификатор окна в Win32.
Цикл сообщенийБесконечный цикл: ОС присылает события (клик, закрытие), программа их обрабатывает.
COM / WinRTМодель объектов Microsoft для современных API (WinUI, часть системных служб).

Карта вариантов

ПодходПлюсыМинусы
Win32 / windows-rsмаксимальная интеграция с ОС, без лишних слоёвмного кода, ручной цикл сообщений, много unsafe
WinUI 3 / WinRTсовременный Fluent, Microsoft Storeв основном Windows, крутая кривая COM
Tauriзнакомый HTML/CSS, относительно малый установщикWebView2, настройка IPC и прав
Slint / Iced / eguiUI на Rust, кроссплатформасвой виджетный набор, реже "родные" контролы
Dioxus + WebViewReact-подобная модельмолодая экосистема, API меняется

Когда имеет смысл нативный API (Win32, windows-rs)

Подходит, если нужно:

  • встроить Rust-модуль в существующее Win32-приложение;
  • вызвать редкий системный API без готовой safe-обёртки;
  • минимальный размер и нулевая зависимость от WebView.

Типичная схема: crate windows или windows-sys даёт объявления функций; тонкий слой unsafe изолируют в одном модуле; бизнес-логика остаётся на safe Rust (FFI и дисциплина). Цикл GetMessage / DispatchMessage держат в одном месте; обработчики WM_* остаются короткими.

Для первого знакомства с языком ручной Win32 — тяжёлый старт. Сначала освойте Cargo и ownership, затем возвращайтесь к нативному UI при явной необходимости.


Когда Tauri или другой WebView

  • команда уже пишет интерфейс на React / Vue / Svelte;
  • нужен установщик под Windows, macOS и Linux из одной кодовой базы;
  • важны автообновления, системные диалоги, разграничение прав.

Rust отвечает за файлы, сеть, криптографию, работу с ОС; UI живёт в изолированном WebView. Безопасность строят на явном списке команд (capabilities): JavaScript не получает произвольный доступ к диску — только то, что вы экспортировали в Rust.

На Windows Tauri опирается на WebView2 (движок Edge Chromium). На свежих Windows 10/11 runtime часто уже установлен; иначе нужен bootstrap-установщик Microsoft. Это компромисс: полный Electron не тащите, но компонент WebView2 остаётся зависимостью.

Команда Rust в Tauri (IPC):

#[tauri::command]
fn read_config() -> Result<String, String> {
std::fs::read_to_string("config.json").map_err(|e| e.to_string())
}
{
"permissions": [
"core:default",
"fs:allow-read-text-file"
]
}

Разбор:

  • #[tauri::command] экспортирует Rust-функцию для вызова из JavaScript.
  • read_config читает файл и возвращает Result, чтобы ошибки были явными.
  • JS не получает прямой доступ к диску: только через разрешённые команды.
  • Блок permissions в capabilities ограничивает, что можно делать из UI.
  • Такой подход снижает риск произвольного доступа к файловой системе.

Когда Slint, Iced, egui

  • интерфейс должен выглядеть одинаково на Linux, macOS и Windows;
  • нельзя или нежелательно тянуть Chromium/WebView2;
  • HMI, встраиваемые панели, внутренние утилиты.
ФреймворкИдея
Slintдекларативные .slint-файлы, компиляция в Rust
Icedархитектура в духе Elm, рендер через wgpu/OpenGL
eguiimmediate mode — удобно для редакторов и отладочных панелей

"Immediate mode" в egui значит: каждый кадр вы заново описываете, какие кнопки нарисовать; состояние храните сами. Подходит для инструментов, где важна скорость разработки, а не пиксель-перфект как у системных диалогов.

Минимальный пример egui (eframe):

Код ITЗагрузка примера кода…

Разбор:

  • eframe::run_native создаёт окно и запускает UI-цикл.
  • MyApp хранит состояние интерфейса (clicks).
  • update вызывается каждый кадр и перерисовывает панель.
  • ui.button(...).clicked() обрабатывает нажатие кнопки.
  • ui.label(...) показывает текущее значение счётчика.

Подключение:

cargo add eframe

Разбор:

  • Команда добавляет зависимость в Cargo.toml и скачивает crate.
  • После этого можно сразу собирать и запускать GUI-пример.
  • Для Windows обычно достаточно стандартного toolchain Rust.
  • Версию eframe лучше фиксировать осознанно при переходе в продакшен.
  • Это самый быстрый способ получить живое окно для прототипа.

Практическая рекомендация по сценарию

  1. Утилита или прототипegui (eframe) или Iced: быстрый старт, один cargo add.
  2. Продукт с веб-командойTauri.
  3. Промышленный HMI, встраиваемые экраныSlint.
  4. Интеграция в Win32/COM legacywindows-rs + FFI.
  5. Microsoft Store, Fluent, глубокая интеграция с Windows 11WinUI 3 (отдельная траектория; опыт C#/C++ с XAML сильно помогает).

Частые ошибки

СимптомВероятная причинаЧто сделать
WebView2 missingна машине нет runtimeустановить WebView2 Runtime или bootstrap из документации Tauri
Паника в Win32 unsafeневерный HWND или вызов не из UI-потокапроверить поток создания окна; Win32 UI обычно однопоточный
Tauri IPC deniedкоманда не в allowlist capabilitiesдобавить команду в конфиг capabilities
Размытый egui на 4KDPI scalingнастроить with_pixels_per_point в eframe

Что попробовать руками

  1. egui — проект eframe, одно окно с кнопкой (cargo add eframe).
  2. Tauri — шаблон create-tauri-app, сравните размер установки с Electron.
  3. Логику без UI сначала вынесите в библиотеку и проверьте через Axum или тесты — UI подключите позже.

Разделение "ядро без UI" + desktop-оболочка:

// crates/core/src/lib.rs
pub fn build_report(input: &str) -> String {
format!("Отчёт: {}", input.trim())
}
// ui/main.rs (egui/tauri/win32 — неважно)
let text = core::build_report("заказ #42");

Разбор:

  • Бизнес-логика живёт в отдельном crate и не зависит от GUI-фреймворка.
  • UI-слой только отображает результат и обрабатывает события пользователя.
  • Один и тот же core можно тестировать unit-тестами и через HTTP API.
  • При смене стека (egui -> Tauri) переписывается в основном UI-обвязка.
  • Это снижает стоимость экспериментов со стеком на раннем этапе.

Критерии выбора для команды

Перед стартом полезно зафиксировать несколько критериев, чтобы решение было инженерным, а не "по вкусу":

КритерийЧто спросить
Командакто будет делать UI: web-разработчики или системные разработчики
Срокинужен быстрый MVP или долгий продуктовый цикл
Платформытолько Windows или сразу Windows/macOS/Linux
Деплойкорпоративный MSI, Store, portable-архив
Интеграция с ОСнужны ли deep-link, tray, COM, системные уведомления, драйверы

Эта таблица помогает обосновать выбор стека перед архитекторами и продуктом.


Поддержка и эксплуатация

У desktop-приложений много полевых нюансов:

  • автообновления и откат версии при неудачном релизе;
  • сбор crash-логов и телеметрии;
  • проверка подписи бинарников;
  • политика обновления WebView2 в корпоративной сети.

Эти вопросы влияют на выбор стека не меньше, чем синтаксис API.


Мини-чек-лист перед началом разработки

  1. Выбран baseline Windows (10/11, версия сборки).
  2. Описан способ установки и обновления.
  3. Согласован формат логов и канал отправки ошибок.
  4. Ядро логики вынесено в отдельный crate без UI.
  5. Есть пилотный экран и замер запуска на слабой машине.

Если пройти этот список в начале, проект по GUI обычно проходит интеграцию заметно спокойнее.


Связанные материалы