• Build For Fun
  • Posts
  • Rust in 2024: Navigating Challenges, Reaping Rewards

Rust in 2024: Navigating Challenges, Reaping Rewards

Rust is the future of systems programming.

Rust is the future of systems programming.

Support our Open-Source Project used in this blog post to teach you Rust.

Intro

Learning Rust in 2024 has become an adventure marked by challenges and, more importantly, substantial rewards.

The codebase we’ll be exploring serves as a guide through the intricate landscape of Rust programming, showcasing its capabilities in creating a dynamic and functional application.

Core Functionalities: Monitors, Utilities, UI, Interface, Backend, and Configuration

The modular structure is a Rust best practice, promoting code separation and reusability.

From handling monitors and configuring the application to managing the user interface and backend operations, Rust’s module system allows for a well-organized codebase.

fn run() -> Result<()> { unsafe { RoInitialize(RO_INIT_SINGLETHREADED)? }; let _xaml_manager = WindowsXamlManager::InitializeForCurrentThread()?; let config = Arc::new(Mutex::new(Config::load()?)); let event_loop = EventLoopBuilder::with_user_event().build()?; event_loop.listen_device_events(DeviceEvents::Never); let controller = MonitorController::new(&event_loop, config.clone()); let _tray = TrayIconBuilder::new() .with_tooltip("Change Brightness") .with_icon(Icon::from_rgba(generate_circle_icon_rgba(32), 32, 32)?) .with_menu(Menu::new([MenuItem::button("Quit", CustomEvent::Quit)])) .build_event_loop(&event_loop, |event| match event { TrayEvent::Tray(ClickType::Left) => Some(CustomEvent::Show), TrayEvent::Menu(e) => Some(e), _ => None })?;

The run function serves as the entry point, encapsulating the application's core logic.

This function ensures a safe initialization of the Windows Runtime (WinRT), initializes the XAML manager, and orchestrates the overall functionality of the application.

The main function, in typical Rust fashion, is concise and clear, handling initialization, error management, and exit codes.

fn main() -> ExitCode { panic::set_hook(); logger::init(LevelFilter::Trace, LevelFilter::Warn); match run() { Ok(()) => ExitCode::SUCCESS, Err(err) => { log::error!("{:?}", err); panic::show_msg(format_args!("{}\n at {}", err.message(), err.trace())); ExitCode::FAILURE } }}

Support our Open-Source Project used in this blog post to teach you Rust.

User Events and Tray Icon Integration

pub enum CustomEvent { Quit, Show, FocusLost, RegisterMonitor(String, MonitorPath), UpdateBrightness(MonitorPath, u32)}

Rust’s enum type is leveraged to define custom events, offering a clear and structured way to handle different application states.

Events such as quitting, showing the application, losing focus, registering monitors, and updating brightness are neatly encapsulated, enhancing code readability.

GUI Development with Winit and Windows API

let window = WindowBuilder::new() .with_title("XAML Window") .with_position(PhysicalPosition::new(1000000, 0)) .with_inner_size(PhysicalSize::new(400, 250)) .with_no_redirection_bitmap(true) .with_decorations(false) .with_undecorated_shadow(true) .with_skip_taskbar(true) .with_visible(true) .with_resizable(false) .with_enabled_buttons(WindowButtons::empty()) .build(&event_loop)?; let mut gui = XamlGui::new(&window)?; FocusManager::LosingFocus(&EventHandler::new({ let proxy = event_loop.create_proxy(); move |_e, arg: &Option<LosingFocusEventArgs>| { if arg.as_ref().some()?.NewFocusedElement().is_err() { proxy .send_event(CustomEvent::FocusLost) .unwrap_or_else(|err| log::warn!("Failed to forward event: {}", err)); } Ok(()) } }))?;

Rust seamlessly integrates with Winit for window management, providing a platform-agnostic solution.

The XamlGui struct demonstrates the integration of Rust with the Windows API for GUI development, showcasing the language's versatility.

Support our Open-Source Project used in this blog post to teach you Rust.

Rust’s Unique Features in Action

Error Handling and Result Types

fn run() -> Result<()> { unsafe { RoInitialize(RO_INIT_SINGLETHREADED)? }; let _xaml_manager = WindowsXamlManager::InitializeForCurrentThread()?; let config = Arc::new(Mutex::new(Config::load()?)); let event_loop = EventLoopBuilder::with_user_event().build()?; event_loop.listen_device_events(DeviceEvents::Never); let controller = MonitorController::new(&event_loop, config.clone()); let _tray = TrayIconBuilder::new() .with_tooltip("Change Brightness") .with_icon(Icon::from_rgba(generate_circle_icon_rgba(32), 32, 32)?) .with_menu(Menu::new([MenuItem::button("Quit", CustomEvent::Quit)])) .build_event_loop(&event_loop, |event| match event { TrayEvent::Tray(ClickType::Left) => Some(CustomEvent::Show), TrayEvent::Menu(e) => Some(e), _ => None })?; let window = WindowBuilder::new() .with_title("XAML Window") .with_position(PhysicalPosition::new(1000000, 0)) .with_inner_size(PhysicalSize::new(400, 250)) .with_no_redirection_bitmap(true) .with_decorations(false) .with_undecorated_shadow(true) .with_skip_taskbar(true) .with_visible(true) .with_resizable(false) .with_enabled_buttons(WindowButtons::empty()) .build(&event_loop)?; let mut gui = XamlGui::new(&window)?; FocusManager::LosingFocus(&EventHandler::new({ let proxy = event_loop.create_proxy(); move |_e, arg: &Option<LosingFocusEventArgs>| { if arg.as_ref().some()?.NewFocusedElement().is_err() { proxy .send_event(CustomEvent::FocusLost) .unwrap_or_else(|err| log::warn!("Failed to forward event: {}", err)); } Ok(()) } }))?; window.set_visible(true); event_loop .run_result(|event, target| Ok(match event { Event::WindowEvent { event, .. } => match event { WindowEvent::Resized(new) => gui.resize(new)?, WindowEvent::Focused(true) => gui.focus(), _ => {} }, Event::UserEvent(event) => match event { CustomEvent::Quit => { controller.shutdown(); target.exit() }, CustomEvent::Show => { controller.refresh_brightness(); if let Some(workspace) = target.primary_monitor().and_then(|m| m.get_work_area().ok()) { if let Ok(height) = gui.get_required_height() { let _ = window.request_inner_size(PhysicalSize::new( window.outer_size().width, height)); } let gap = 14; let size = window.outer_size(); window.set_outer_position(PhysicalPosition::new( workspace.right - gap - size.width as i32, workspace.bottom - gap - size.height as i32 )); window.set_visible(true); window.set_window_level(WindowLevel::AlwaysOnTop); window.focus_window(); } else { log::warn!("Can't find work area of primary monitor"); } } CustomEvent::FocusLost => { window.set_visible(false); config.lock_no_poison().save_if_dirty()?; } CustomEvent::RegisterMonitor(name, path) => { log::info!("Found monitor: {}", name); gui.register_monitor(name, path, controller.create_proxy())? }, CustomEvent::UpdateBrightness(path, value) => { gui.update_brightness(path, value)?; } }, _ => {} }))?; config.lock_no_poison().save_if_dirty()?; unsafe { RoUninitialize() } Ok(())}

Rust’s emphasis on robust error handling is evident through the use of the Result type.

The ? operator succinctly handles errors, contributing to the creation of reliable and resilient applications.

Ownership and Memory Safety

let config = Arc::new(Mutex::new(Config::load()?));// ... (ownership and borrowing used throughout the code)

Rust’s ownership system and memory safety features are prominent in the code.

The use of Arc (atomic reference counting) + Mutex ensures safe concurrent access to configuration data, preventing data races.

Conclusion: Navigating Rust’s Seas

Learning Rust in 2024 proves to be an enlightening experience.

The codebase not only showcases the language’s features but also demonstrates how Rust’s ownership model, error handling, and modular design contribute to building robust applications.

As Rust continues to evolve, it remains a rewarding language for developers who seek performance, safety, and elegance in their code.

Support our Open-Source Project used in this blog post to teach you Rust.