Quick Reference Guide
Common Rust Patterns Used in This Project
1. Creating a Result-Returning Function
use anyhow::{anyhow, Result};
fn load_config() -> Result<String> {
let path = "/path/to/config";
std::fs::read_to_string(path)
.map_err(|e| anyhow!("Failed to load config: {}", e))
}
// Usage:
match load_config() {
Ok(config) => println!("Loaded: {}", config),
Err(e) => eprintln!("Error: {}", e),
}
// Or use ? operator to propagate:
fn process() -> Result<()> {
let config = load_config()?; // Returns on error
println!("Processing: {}", config);
Ok(())
}
2. Enum Pattern Matching
#[derive(Debug)]
pub enum CloudService {
GoogleDrive,
OneDrive,
Dropbox,
}
// Match all variants
match service {
CloudService::GoogleDrive => {
println!("Configuring Google Drive");
}
CloudService::OneDrive => {
println!("Configuring OneDrive");
}
CloudService::Dropbox => {
println!("Configuring Dropbox");
}
}
// Exhaustive matching catches errors at compile time!
// If you add a new variant, compiler forces you to handle it.
3. Option and None Handling
// Option<T> = Some(T) or None
let maybe_name: Option<String> = Some("My Drive".to_string());
// Pattern match
match maybe_name {
Some(name) => println!("Name: {}", name),
None => println!("No name set"),
}
// Use unwrap_or for default
let name = maybe_name.unwrap_or_else(|| "Unnamed".to_string());
// Use map to transform
let upper = maybe_name.map(|n| n.to_uppercase());
// Chain operations with ?
fn get_first_remote() -> Option<String> {
let remotes = vec!["drive1", "drive2"];
let first = remotes.first()?; // Returns None if empty
Some(first.to_string())
}
4. Shared Mutable State
use std::sync::{Arc, Mutex};
// Create shared state
let config = Arc::new(Mutex::new(vec![1, 2, 3]));
// Clone for use in closure
let config_clone = config.clone();
button.connect_clicked(move || {
// Lock the mutex
if let Ok(mut cfg) = config_clone.lock() {
cfg.push(4); // Modify safely
println!("Config: {:?}", *cfg);
}
// Lock released when cfg dropped
});
Important:
Arc= Atomic Reference Count (safe to share)Mutex= Mutual exclusion (safe concurrent access).lock()returnsResult<MutexGuard>- Lock automatically releases when guard drops
5. String Manipulation
// Create strings
let s1 = String::from("hello");
let s2 = "hello".to_string();
let s3 = format!("hello {}", "world");
// String slices (don't own data)
let slice: &str = "hello";
// Split and parse
let line = "key = value";
if let Some((key, val)) = line.split_once('=') {
let key = key.trim();
let val = val.trim();
println!("{}={}", key, val);
}
// Join collection
let parts = vec!["a", "b", "c"];
let joined = parts.join(", "); // "a, b, c"
// Replace patterns
let text = "old text";
let replaced = text.replace("old", "new");
// Contains and starts_with
if text.contains("test") {
println!("Found!");
}
if text.starts_with("test") {
println!("Starts with test");
}
6. Iterating and Collecting
let numbers = vec![1, 2, 3, 4, 5];
// Simple iteration
for num in &numbers {
println!("{}", num);
}
// Transform with map
let doubled: Vec<i32> = numbers.iter().map(|n| n * 2).collect();
// Filter and map
let evens: Vec<i32> = numbers
.iter()
.filter(|n| n % 2 == 0)
.map(|n| n * 10)
.collect();
// Find first matching
if let Some(first) = numbers.iter().find(|n| n > &&3) {
println!("First > 3: {}", first);
}
// Fold/reduce
let sum = numbers.iter().fold(0, |acc, n| acc + n);
// Lines from string
let text = "line1\nline2\nline3";
for line in text.lines() {
println!("{}", line);
}
7. File Operations
use std::fs;
use std::path::Path;
// Read entire file
let content = fs::read_to_string("/path/to/file")?;
// Write entire file (overwrites)
fs::write("/path/to/file", "content")?;
// Check if exists
if Path::new("/path/to/file").exists() {
println!("File exists");
}
// Create directory
fs::create_dir_all("/path/to/dir")?;
// List directory
for entry in fs::read_dir("/path/to/dir")? {
let entry = entry?;
let path = entry.path();
println!("{:?}", path);
}
// Get home directory
if let Some(home) = dirs::home_dir() {
println!("Home: {:?}", home);
}
// Get config directory (~/.config)
if let Some(config) = dirs::config_dir() {
println!("Config: {:?}", config);
}
8. Running External Commands
use std::process::Command;
// Simple execution
let output = Command::new("systemctl")
.args(&["--user", "list-units"])
.output()?;
if !output.status.success() {
let error = String::from_utf8_lossy(&output.stderr);
return Err(anyhow!("Command failed: {}", error));
}
let stdout = String::from_utf8_lossy(&output.stdout);
println!("Output: {}", stdout);
// Parse output
for line in stdout.lines() {
let parts: Vec<&str> = line.split_whitespace().collect();
if let Some(service_name) = parts.first() {
println!("Service: {}", service_name);
}
}
9. Type Conversion
// String to number
let num_str = "42";
let num: i32 = num_str.parse()?; // Result<i32, ParseIntError>
// Number to string
let num = 42;
let str1 = num.to_string();
let str2 = format!("{}", num);
// Type conversion with as
let byte: u8 = 255;
let int: i32 = byte as i32;
// Convert between Result and Option
let result: Result<i32> = Ok(42);
let option: Option<i32> = result.ok();
let option: Option<i32> = Some(42);
let result: Result<i32, &str> = option.ok_or("No value");
10. Logging
use tracing::{info, debug, warn, error, trace};
// Setup (in main)
tracing_subscriber::fmt()
.with_max_level(tracing::Level::DEBUG)
.init();
// Use anywhere
info!("Application started");
debug!("Debug details: {:?}", some_value);
warn!("Warning: this might fail");
error!("Error occurred: {}", err);
trace!("Very detailed trace");
// With structured fields
info!(
remotes = 5,
mounts = 3,
"System state"
);
// Span for tracking function execution
use tracing::instrument;
#[instrument]
fn process_remote(remote: &RemoteConfig) -> Result<()> {
info!("Processing remote");
// All logs from here are tagged with this function
Ok(())
}
GTK4 Common Code Snippets
Creating Widgets
use gtk4::prelude::*;
use gtk4::*;
// Labels
let label = Label::new(Some("Text"));
label.set_wrap(true);
label.add_css_class("title-1");
// Buttons
let button = Button::with_label("Click Me");
button.add_css_class("suggested-action");
button.set_sensitive(false); // Disable
// Entry (text input)
let entry = Entry::new();
entry.set_placeholder_text(Some("Enter text"));
let text = entry.text();
// ComboBox
let combo = ComboBoxText::new();
combo.append_text("Option 1");
combo.append_text("Option 2");
combo.set_active(Some(0));
// Containers
let vbox = Box::new(Orientation::Vertical, 12);
let hbox = Box::new(Orientation::Horizontal, 6);
// Scrolling
let scrolled = ScrolledWindow::new();
scrolled.set_child(Some(&content_widget));
// List
let list = ListBox::new();
list.set_selection_mode(SelectionMode::Single);
// Add rows programmatically
// Expander (collapsible section)
let expander = Expander::new(Some("Advanced"));
expander.set_child(Some(&advanced_content));
Layout and Positioning
// Set size
widget.set_width_request(200);
widget.set_height_request(100);
// Set margins
widget.set_margin_top(12);
widget.set_margin_bottom(12);
widget.set_margin_start(12);
widget.set_margin_end(12);
// Expansion
widget.set_hexpand(true); // Fill horizontal space
widget.set_vexpand(true); // Fill vertical space
// Alignment
widget.set_halign(Align::Start); // Left
widget.set_halign(Align::Center); // Center
widget.set_halign(Align::End); // Right
widget.set_valign(Align::Start);
widget.set_valign(Align::Center);
widget.set_valign(Align::End);
// Append to containers
container.append(&widget);
container.remove(&widget);
// Set parent
window.set_child(Some(&widget));
Styling with CSS
// Define CSS
let css = r#"
button {
padding: 6px 12px;
border-radius: 4px;
}
button.destructive-action {
background-color: #e74c3c;
color: white;
}
label.title-1 {
font-size: 28px;
font-weight: bold;
}
.monospace {
font-family: monospace;
font-size: 11px;
}
"#;
// Apply CSS
let provider = CssProvider::new();
provider.load_from_data(css);
if let Some(display) = gdk::Display::default() {
gtk4::style_context_add_provider_for_display(
&display,
&provider,
gtk4::STYLE_PROVIDER_PRIORITY_APPLICATION,
);
}
// Apply class to widget
widget.add_css_class("destructive-action");
widget.add_css_class("monospace");
// Remove class
widget.remove_css_class("destructive-action");
Signals and Events
use gtk4::prelude::*;
// Button click
button.connect_clicked(|btn| {
println!("Button clicked!");
btn.set_label("Clicked!");
});
// Entry text changed
entry.connect_changed(|entry| {
let text = entry.text();
println!("Text: {}", text);
});
// ComboBox selection changed
combo.connect_changed(|combo| {
if let Some(text) = combo.active_text() {
println!("Selected: {}", text);
}
});
// List selection changed
list.connect_row_selected(|_list, row| {
if let Some(row) = row {
println!("Selected row: {}", row.index());
}
});
// Window close requested
window.connect_close_request(|_| {
println!("Window closing");
glib::signal::Inhibit(false) // Allow close
});
// Key press
widget.connect_key_pressed(|_widget, key, _code, _state| {
match key {
gdk::Key::Escape => {
println!("Escape pressed");
glib::signal::Inhibit(true) // Stop propagation
}
_ => glib::signal::Inhibit(false),
}
});
Dialogs
// Error dialog
let dialog = MessageDialog::new(
Some(&window),
gtk4::DialogFlags::MODAL,
gtk4::MessageType::Error,
gtk4::ButtonsType::Ok,
"Error Title",
);
dialog.set_secondary_text(Some("Error details"));
dialog.run_async(|dialog, _| dialog.close());
// Confirmation
let dialog = MessageDialog::new(
Some(&window),
gtk4::DialogFlags::MODAL,
gtk4::MessageType::Question,
gtk4::ButtonsType::YesNo,
"Confirm?",
);
dialog.set_secondary_text(Some("Really do this?"));
let response = dialog.run();
if response == ResponseType::Yes as i32 {
println!("User said yes");
}
// File chooser
let dialog = FileChooserDialog::new(
Some("Open File"),
Some(&window),
gtk4::FileChooserAction::Open,
&[("Cancel", ResponseType::Cancel as i32), ("Open", ResponseType::Accept as i32)],
);
let response = dialog.run();
if response == ResponseType::Accept as i32 {
if let Some(file) = dialog.file() {
if let Some(path) = file.path() {
println!("Selected: {:?}", path);
}
}
}
Debian Packaging Quick Start
Minimal debian/control
Source: myapp
Section: utils
Priority: optional
Maintainer: Your Name <you@example.com>
Build-Depends: cargo, rustc
Standards-Version: 4.7.0
Rules-Requires-Root: no
Package: myapp
Architecture: amd64 arm64
Depends: ${misc:Depends}
Description: My application
Short description (continued)
with details.
Minimal debian/rules
#!/usr/bin/make -f
%:
dh $@
override_dh_auto_build:
cargo build --release --locked
override_dh_auto_test:
cargo test --release || true
override_dh_auto_clean:
cargo clean || true
dh_auto_clean
Build commands
# Build Debian package
dpkg-buildpackage -b -uc -us
# Build source package
dpkg-buildpackage -S -uc -us
# Install locally
sudo dpkg -i ../myapp_1.0-1_amd64.deb
# Uninstall
sudo dpkg -r myapp
# Check package contents
dpkg -c myapp_1.0-1_amd64.deb
# Get package info
dpkg -I myapp_1.0-1_amd64.deb
Testing Patterns
Unit Test
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_service() {
let service_str = "drive";
let service = parse_service_type(service_str);
assert_eq!(service, CloudService::GoogleDrive);
}
#[test]
fn test_remote_creation() {
let remote = RemoteConfig::new(
"test".to_string(),
CloudService::GoogleDrive,
);
assert_eq!(remote.name, "test");
assert!(!remote.auth_method.is_empty());
}
#[test]
#[should_panic(expected = "Failed")]
fn test_panic() {
panic!("Failed");
}
}
Run tests
cargo test # All tests
cargo test --lib # Library tests only
cargo test specific_test # One test
cargo test -- --nocapture # Show println! output
cargo test -- --test-threads=1 # Single-threaded
Troubleshooting
“Cannot find gtk4 libraries”
# Install dev packages
sudo apt-get install libgtk-4-dev libadwaita-1-dev
# Set pkg-config path if needed
export PKG_CONFIG_PATH=/usr/lib/x86_64-linux-gnu/pkgconfig:$PKG_CONFIG_PATH
# Verify
pkg-config --list-all | grep gtk4
“Value moved into closure”
// Problem
let value = String::from("test");
button.connect_clicked(|| {
println!("{}", value); // ❌ value moved
});
// Solution 1: Clone
let value_clone = value.clone();
button.connect_clicked(move || {
println!("{}", value_clone); // ✅ Using clone
});
// Solution 2: Use Arc for complex types
let value = Arc::new(value);
let value_clone = value.clone();
button.connect_clicked(move || {
println!("{}", value_clone);
});
“Unwrap panicked”
// Bad
let value = operation().unwrap(); // Panics if error
// Good
match operation() {
Ok(v) => { /* use v */ }
Err(e) => eprintln!("Error: {}", e),
}
// Or propagate
let value = operation()?; // Returns if error
Binary too large
# Strip debug symbols
cargo build --release
strip target/release/myapp
# Check size before/after
ls -lh target/release/myapp
# Profile size usage
cargo bloat --release
Performance Profiling
Check binary size
cargo build --release
ls -lh target/release/rclone-config-manager
# Detailed breakdown
cargo bloat --release
# Find largest functions
nm -rS target/release/rclone-config-manager | head -20
Profile runtime
# Linux perf
sudo perf record ./target/release/myapp
sudo perf report
# Valgrind (memory)
valgrind --leak-check=full ./target/release/myapp
# Time execution
time ./target/release/myapp
# Monitor resources
top
htop
watch -n 1 'ps aux | grep myapp'
Git Workflow
# See what changed
git status
git diff
# Stage changes
git add file.rs
git add . # All changes
# Commit with message
git commit -m "Fix bug in config parsing"
# View history
git log
git log --oneline
git log -p # Show diffs
# Create branch
git checkout -b feature/oauth
# Switch branches
git checkout main
git switch feature/oauth
# Merge branch
git merge feature/oauth
# Push to remote
git push origin main
Resources
- Rust Book: https://doc.rust-lang.org/book/
- GTK4 Rust: https://gtk-rs.org/gtk4-rs/
- Tokio Async: https://tokio.rs/
- Error Handling: https://doc.rust-lang.org/rust-by-example/error.html
- Debian Guide: https://www.debian.org/doc/debian-policy/
This guide provides copy-paste ready patterns for common tasks!