Application Development with Rust for Enterprise Systems Link to heading
Building enterprise applications requires a diverse set of tools and approaches depending on the target platform and user interface requirements. In this post, we’ll explore how Rust can be used to develop web, CLI, desktop, and mobile applications for enterprise environments.
Web Application Development Link to heading
Web applications are a common requirement for enterprise systems. Rust offers several approaches to building web frontends, from server-side rendering to WebAssembly.
Server-Side Rendering with Templates Link to heading
For traditional server-rendered applications, Rust provides several templating engines:
Tera Templates Link to heading
Tera is inspired by Jinja2 and Django templates, making it familiar to developers from Python backgrounds:
use axum::{
extract::State,
response::Html,
routing::get,
Router,
};
use serde::Serialize;
use std::sync::Arc;
use tera::{Context, Tera};
#[derive(Serialize)]
struct User {
id: u64,
name: String,
email: String,
}
struct AppState {
tera: Tera,
users: Vec<User>,
}
async fn users_page(State(state): State<Arc<AppState>>) -> Html<String> {
let mut context = Context::new();
context.insert("users", &state.users);
context.insert("title", "User List");
let rendered = state.tera.render("users.html", &context)
.expect("Failed to render template");
Html(rendered)
}
#[tokio::main]
async fn main() {
// Initialize Tera
let mut tera = Tera::default();
tera.add_raw_templates(vec![
("base.html", r#"
<!DOCTYPE html>
<html>
<head>
<title>{{ title }}</title>
</head>
<body>
<header>
<h1>{{ title }}</h1>
</header>
<main>
{% block content %}{% endblock content %}
</main>
<footer>
<p>© 2025 Enterprise App</p>
</footer>
</body>
</html>
"#),
("users.html", r#"
{% extends "base.html" %}
{% block content %}
<table>
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Email</th>
</tr>
</thead>
<tbody>
{% for user in users %}
<tr>
<td>{{ user.id }}</td>
<td>{{ user.name }}</td>
<td>{{ user.email }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock content %}
"#),
]).expect("Failed to add templates");
// Sample data
let users = vec![
User { id: 1, name: "Alice".to_string(), email: "alice@example.com".to_string() },
User { id: 2, name: "Bob".to_string(), email: "bob@example.com".to_string() },
];
// Create app state
let state = Arc::new(AppState { tera, users });
// Build our application
let app = Router::new()
.route("/users", get(users_page))
.with_state(state);
// Run our server
let addr = "0.0.0.0:3000";
println!("Server running on http://{}", addr);
axum::Server::bind(&addr.parse().unwrap())
.serve(app.into_make_service())
.await
.unwrap();
}
Askama Templates Link to heading
Askama compiles templates to Rust code, providing type safety and performance:
use askama::Template;
use axum::{
response::{Html, IntoResponse},
routing::get,
Router,
};
#[derive(Template)]
#[template(path = "users.html")]
struct UsersTemplate {
title: String,
users: Vec<User>,
}
struct User {
id: u64,
name: String,
email: String,
}
async fn users_page() -> impl IntoResponse {
let users = vec![
User { id: 1, name: "Alice".to_string(), email: "alice@example.com".to_string() },
User { id: 2, name: "Bob".to_string(), email: "bob@example.com".to_string() },
];
let template = UsersTemplate {
title: "User List".to_string(),
users,
};
Html(template.render().unwrap())
}
#[tokio::main]
async fn main() {
// Build our application
let app = Router::new()
.route("/users", get(users_page));
// Run our server
let addr = "0.0.0.0:3000";
println!("Server running on http://{}", addr);
axum::Server::bind(&addr.parse().unwrap())
.serve(app.into_make_service())
.await
.unwrap();
}
WebAssembly Frontend Development Link to heading
For more interactive web applications, Rust can compile to WebAssembly (Wasm), enabling rich client-side experiences:
Yew Framework Link to heading
Yew is a modern Rust framework for creating multi-threaded frontend web apps with WebAssembly:
use yew::prelude::*;
#[derive(Clone, PartialEq, Properties)]
struct UserProps {
id: u64,
name: String,
email: String,
}
#[function_component(UserRow)]
fn user_row(props: &UserProps) -> Html {
html! {
<tr>
<td>{ props.id }</td>
<td>{ &props.name }</td>
<td>{ &props.email }</td>
</tr>
}
}
#[function_component(UserList)]
fn user_list() -> Html {
let users = vec![
UserProps { id: 1, name: "Alice".to_string(), email: "alice@example.com".to_string() },
UserProps { id: 2, name: "Bob".to_string(), email: "bob@example.com".to_string() },
];
html! {
<div>
<h1>{ "User List" }</h1>
<table>
<thead>
<tr>
<th>{ "ID" }</th>
<th>{ "Name" }</th>
<th>{ "Email" }</th>
</tr>
</thead>
<tbody>
{ for users.iter().map(|user| html! { <UserRow ..user.clone() /> }) }
</tbody>
</table>
</div>
}
}
#[function_component(App)]
fn app() -> Html {
html! {
<div>
<header>
<h1>{ "Enterprise App" }</h1>
</header>
<main>
<UserList />
</main>
<footer>
<p>{ "© 2025 Enterprise App" }</p>
</footer>
</div>
}
}
fn main() {
yew::Renderer::<App>::new().render();
}
Dioxus Framework Link to heading
Dioxus is a portable, performant, and ergonomic framework for building cross-platform user interfaces:
use dioxus::prelude::*;
#[derive(Props, PartialEq)]
struct UserProps {
id: u64,
name: String,
email: String,
}
fn UserRow(cx: Scope<UserProps>) -> Element {
cx.render(rsx! {
tr {
td { "{cx.props.id}" }
td { "{cx.props.name}" }
td { "{cx.props.email}" }
}
})
}
fn UserList(cx: Scope) -> Element {
let users = vec![
UserProps { id: 1, name: "Alice".to_string(), email: "alice@example.com".to_string() },
UserProps { id: 2, name: "Bob".to_string(), email: "bob@example.com".to_string() },
];
cx.render(rsx! {
div {
h1 { "User List" }
table {
thead {
tr {
th { "ID" }
th { "Name" }
th { "Email" }
}
}
tbody {
for user in users {
UserRow {
id: user.id,
name: user.name,
email: user.email,
}
}
}
}
}
})
}
fn App(cx: Scope) -> Element {
cx.render(rsx! {
div {
header {
h1 { "Enterprise App" }
}
main {
UserList {}
}
footer {
p { "© 2025 Enterprise App" }
}
}
})
}
fn main() {
dioxus_web::launch(App);
}
Full-Stack Rust Applications Link to heading
For enterprise applications, a full-stack Rust approach can provide consistency and type safety across the entire application:
// Shared types between frontend and backend
#[derive(Serialize, Deserialize, Clone, PartialEq)]
struct User {
id: u64,
name: String,
email: String,
}
// Backend API endpoint
async fn get_users() -> Json<Vec<User>> {
// Fetch from database
Json(vec![
User { id: 1, name: "Alice".to_string(), email: "alice@example.com".to_string() },
User { id: 2, name: "Bob".to_string(), email: "bob@example.com".to_string() },
])
}
// Frontend component using the same User type
#[function_component(UserList)]
fn user_list() -> Html {
let users = use_state(|| Vec::<User>::new());
{
let users = users.clone();
use_effect_with_deps(move |_| {
wasm_bindgen_futures::spawn_local(async move {
let fetched_users: Vec<User> = Request::get("/api/users")
.send()
.await
.unwrap()
.json()
.await
.unwrap();
users.set(fetched_users);
});
|| ()
}, ());
}
html! {
<div>
<h1>{ "User List" }</h1>
<table>
<thead>
<tr>
<th>{ "ID" }</th>
<th>{ "Name" }</th>
<th>{ "Email" }</th>
</tr>
</thead>
<tbody>
{ for users.iter().map(|user| html! {
<tr>
<td>{ user.id }</td>
<td>{ &user.name }</td>
<td>{ &user.email }</td>
</tr>
}) }
</tbody>
</table>
</div>
}
}
CLI Application Development Link to heading
Command-line interfaces (CLIs) are essential for enterprise applications, especially for administrative tasks, batch processing, and automation. Rust excels at building robust CLI tools.
Building CLI Applications with Clap Link to heading
Clap is the most popular command-line argument parser for Rust:
use clap::{Parser, Subcommand};
use std::path::PathBuf;
#[derive(Parser)]
#[command(name = "enterprise-cli")]
#[command(about = "Enterprise CLI tool", long_about = None)]
struct Cli {
/// Optional name to operate on
#[arg(short, long)]
name: Option<String>,
/// Sets a custom config file
#[arg(short, long, value_name = "FILE")]
config: Option<PathBuf>,
/// Turn debugging information on
#[arg(short, long, action = clap::ArgAction::Count)]
debug: u8,
#[command(subcommand)]
command: Option<Commands>,
}
#[derive(Subcommand)]
enum Commands {
/// Add a new user
Add {
/// User name
#[arg(short, long)]
name: String,
/// User email
#[arg(short, long)]
email: String,
},
/// List all users
List {
/// Filter by role
#[arg(short, long)]
role: Option<String>,
/// Limit the number of results
#[arg(short, long, default_value_t = 10)]
limit: usize,
},
}
fn main() {
let cli = Cli::parse();
// You can check the value provided by positional arguments, or option arguments
if let Some(name) = cli.name.as_deref() {
println!("Value for name: {name}");
}
if let Some(config_path) = cli.config.as_deref() {
println!("Value for config: {}", config_path.display());
}
// You can see how many times a particular flag or argument occurred
// Note, only flags can have multiple occurrences
match cli.debug {
0 => println!("Debug mode is off"),
1 => println!("Debug mode is kind of on"),
2 => println!("Debug mode is on"),
_ => println!("Don't be crazy"),
}
// You can check for the existence of subcommands, and if found use their
// matches just as you would the top level cmd
match &cli.command {
Some(Commands::Add { name, email }) => {
println!("Adding user {} with email {}", name, email);
// Add user to database
}
Some(Commands::List { role, limit }) => {
println!("Listing users");
if let Some(role) = role {
println!("Filtering by role: {}", role);
}
println!("Limiting to {} results", limit);
// List users from database
}
None => {}
}
}
Interactive CLI Applications Link to heading
For more interactive CLI experiences, Rust offers several libraries:
Dialoguer for User Input Link to heading
use dialoguer::{Input, Password, Select, Confirm, MultiSelect};
fn main() {
// Simple input
let name: String = Input::new()
.with_prompt("Enter your name")
.default("John Doe".into())
.interact_text()
.unwrap();
// Password input
let password = Password::new()
.with_prompt("Enter your password")
.with_confirmation("Confirm password", "Passwords don't match")
.interact()
.unwrap();
// Selection
let options = vec!["Option 1", "Option 2", "Option 3"];
let selection = Select::new()
.with_prompt("Select an option")
.default(0)
.items(&options)
.interact()
.unwrap();
// Confirmation
let confirmed = Confirm::new()
.with_prompt("Do you want to continue?")
.default(true)
.interact()
.unwrap();
// Multi-select
let items = vec!["Item 1", "Item 2", "Item 3", "Item 4"];
let selections = MultiSelect::new()
.with_prompt("Select multiple items")
.items(&items)
.defaults(&[true, false, true, false])
.interact()
.unwrap();
println!("Name: {}", name);
println!("Password: {}", "*".repeat(password.len()));
println!("Selected option: {}", options[selection]);
println!("Confirmed: {}", confirmed);
println!("Selected items: {:?}", selections.iter().map(|&i| items[i]).collect::<Vec<_>>());
}
Indicatif for Progress Bars Link to heading
use indicatif::{ProgressBar, ProgressStyle};
use std::time::Duration;
fn main() {
// Simple progress bar
let pb = ProgressBar::new(100);
pb.set_style(ProgressStyle::default_bar()
.template("{spinner:.green} [{elapsed_precise}] [{bar:40.cyan/blue}] {pos}/{len} ({eta})")
.unwrap()
.progress_chars("#>-"));
for i in 0..100 {
pb.inc(1);
std::thread::sleep(Duration::from_millis(50));
}
pb.finish_with_message("done");
// Multi-progress bar for parallel tasks
let mp = indicatif::MultiProgress::new();
let style = ProgressStyle::default_bar()
.template("{prefix:.bold.dim} {spinner:.green} [{elapsed_precise}] [{bar:40.cyan/blue}] {pos}/{len} ({eta})")
.unwrap()
.progress_chars("#>-");
let pb1 = mp.add(ProgressBar::new(50));
pb1.set_style(style.clone());
pb1.set_prefix("Task 1");
let pb2 = mp.add(ProgressBar::new(100));
pb2.set_style(style.clone());
pb2.set_prefix("Task 2");
let pb3 = mp.add(ProgressBar::new(75));
pb3.set_style(style);
pb3.set_prefix("Task 3");
std::thread::spawn(move || {
for i in 0..50 {
pb1.inc(1);
std::thread::sleep(Duration::from_millis(100));
}
pb1.finish_with_message("done");
});
std::thread::spawn(move || {
for i in 0..100 {
pb2.inc(1);
std::thread::sleep(Duration::from_millis(50));
}
pb2.finish_with_message("done");
});
for i in 0..75 {
pb3.inc(1);
std::thread::sleep(Duration::from_millis(75));
}
pb3.finish_with_message("done");
mp.join().unwrap();
}
Building Robust CLI Applications Link to heading
Enterprise CLI applications require additional considerations:
Error Handling Link to heading
use thiserror::Error;
use clap::Parser;
use std::path::PathBuf;
use std::fs;
#[derive(Error, Debug)]
enum CliError {
#[error("IO error: {0}")]
Io(#[from] std::io::Error),
#[error("Config error: {0}")]
Config(String),
#[error("Validation error: {0}")]
Validation(String),
}
#[derive(Parser)]
struct Cli {
#[arg(short, long)]
config: PathBuf,
}
fn run() -> Result<(), CliError> {
let cli = Cli::parse();
// Check if config file exists
if !cli.config.exists() {
return Err(CliError::Config(format!("Config file not found: {}", cli.config.display())));
}
// Read config file
let content = fs::read_to_string(&cli.config)?;
// Validate config
if content.trim().is_empty() {
return Err(CliError::Validation("Config file is empty".to_string()));
}
// Process config
println!("Config loaded successfully");
Ok(())
}
fn main() {
if let Err(e) = run() {
eprintln!("Error: {}", e);
std::process::exit(1);
}
}
Configuration Management Link to heading
use config::{Config, ConfigError, File};
use serde::Deserialize;
use std::path::Path;
#[derive(Debug, Deserialize)]
struct Database {
url: String,
username: String,
password: String,
}
#[derive(Debug, Deserialize)]
struct Server {
host: String,
port: u16,
}
#[derive(Debug, Deserialize)]
struct Settings {
debug: bool,
database: Database,
server: Server,
}
fn load_config<P: AsRef<Path>>(config_path: P) -> Result<Settings, ConfigError> {
let config = Config::builder()
// Start with default values
.set_default("debug", false)?
.set_default("server.host", "127.0.0.1")?
.set_default("server.port", 8080)?
// Add in settings from the config file
.add_source(File::from(config_path.as_ref()))
// Add in settings from environment variables (with a prefix of APP)
// E.g., `APP_DEBUG=1 ./target/app` would set the `debug` key
.add_source(config::Environment::with_prefix("APP").separator("_"))
.build()?;
// Deserialize the configuration into our Settings struct
config.try_deserialize()
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let settings = load_config("config.toml")?;
println!("Debug mode: {}", settings.debug);
println!("Database URL: {}", settings.database.url);
println!("Server: {}:{}", settings.server.host, settings.server.port);
Ok(())
}
Desktop Application Development Link to heading
Enterprise applications often require desktop interfaces for internal tools and administrative applications. Rust offers several frameworks for building cross-platform desktop applications.
Tauri Link to heading
Tauri allows you to build desktop applications with web technologies while leveraging Rust for the backend:
// src-tauri/src/main.rs
#![cfg_attr(
all(not(debug_assertions), target_os = "windows"),
windows_subsystem = "windows"
)]
use serde::{Deserialize, Serialize};
use tauri::{command, State};
use std::sync::Mutex;
#[derive(Serialize, Deserialize, Clone)]
struct User {
id: u64,
name: String,
email: String,
}
struct AppState {
users: Mutex<Vec<User>>,
}
#[command]
fn get_users(state: State<AppState>) -> Vec<User> {
state.users.lock().unwrap().clone()
}
#[command]
fn add_user(state: State<AppState>, name: String, email: String) -> User {
let mut users = state.users.lock().unwrap();
let id = users.len() as u64 + 1;
let user = User {
id,
name,
email,
};
users.push(user.clone());
user
}
fn main() {
let app_state = AppState {
users: Mutex::new(vec![
User { id: 1, name: "Alice".to_string(), email: "alice@example.com".to_string() },
User { id: 2, name: "Bob".to_string(), email: "bob@example.com".to_string() },
]),
};
tauri::Builder::default()
.manage(app_state)
.invoke_handler(tauri::generate_handler![get_users, add_user])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
Frontend (using React):
import { useState, useEffect } from 'react';
import { invoke } from '@tauri-apps/api/tauri';
function App() {
const [users, setUsers] = useState([]);
const [newUser, setNewUser] = useState({ name: '', email: '' });
useEffect(() => {
loadUsers();
}, []);
async function loadUsers() {
const users = await invoke('get_users');
setUsers(users);
}
async function handleAddUser(e) {
e.preventDefault();
await invoke('add_user', {
name: newUser.name,
email: newUser.email
});
setNewUser({ name: '', email: '' });
loadUsers();
}
return (
<div className="container">
<h1>User Management</h1>
<form onSubmit={handleAddUser}>
<input
type="text"
placeholder="Name"
value={newUser.name}
onChange={(e) => setNewUser({ ...newUser, name: e.target.value })}
required
/>
<input
type="email"
placeholder="Email"
value={newUser.email}
onChange={(e) => setNewUser({ ...newUser, email: e.target.value })}
required
/>
<button type="submit">Add User</button>
</form>
<table>
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Email</th>
</tr>
</thead>
<tbody>
{users.map(user => (
<tr key={user.id}>
<td>{user.id}</td>
<td>{user.name}</td>
<td>{user.email}</td>
</tr>
))}
</tbody>
</table>
</div>
);
}
export default App;
Iced Link to heading
Iced is a cross-platform GUI library focused on simplicity and type safety:
use iced::{
button, scrollable, text_input, Button, Column, Container, Element, Length, Row, Scrollable,
Settings, Text, TextInput,
};
#[derive(Default)]
struct UserForm {
name_input: text_input::State,
name_value: String,
email_input: text_input::State,
email_value: String,
add_button: button::State,
}
#[derive(Clone, Debug)]
struct User {
id: u64,
name: String,
email: String,
}
#[derive(Default)]
struct UserList {
users: Vec<User>,
scroll: scrollable::State,
}
#[derive(Default)]
struct UserManager {
user_form: UserForm,
user_list: UserList,
}
#[derive(Debug, Clone)]
enum Message {
NameInputChanged(String),
EmailInputChanged(String),
AddButtonPressed,
}
impl UserManager {
fn new() -> Self {
let mut user_manager = Self::default();
// Add some initial users
user_manager.user_list.users = vec![
User { id: 1, name: "Alice".to_string(), email: "alice@example.com".to_string() },
User { id: 2, name: "Bob".to_string(), email: "bob@example.com".to_string() },
];
user_manager
}
fn update(&mut self, message: Message) {
match message {
Message::NameInputChanged(value) => {
self.user_form.name_value = value;
}
Message::EmailInputChanged(value) => {
self.user_form.email_value = value;
}
Message::AddButtonPressed => {
if !self.user_form.name_value.is_empty() && !self.user_form.email_value.is_empty() {
let id = self.user_list.users.len() as u64 + 1;
self.user_list.users.push(User {
id,
name: self.user_form.name_value.clone(),
email: self.user_form.email_value.clone(),
});
self.user_form.name_value.clear();
self.user_form.email_value.clear();
}
}
}
}
fn view(&mut self) -> Element<Message> {
let title = Text::new("User Management")
.size(30);
let name_input = TextInput::new(
&mut self.user_form.name_input,
"Name",
&self.user_form.name_value,
Message::NameInputChanged,
)
.padding(10);
let email_input = TextInput::new(
&mut self.user_form.email_input,
"Email",
&self.user_form.email_value,
Message::EmailInputChanged,
)
.padding(10);
let add_button = Button::new(
&mut self.user_form.add_button,
Text::new("Add User"),
)
.on_press(Message::AddButtonPressed)
.padding(10);
let form_row = Row::new()
.spacing(10)
.push(name_input)
.push(email_input)
.push(add_button);
let header_row = Row::new()
.spacing(10)
.push(Text::new("ID").width(Length::FillPortion(1)))
.push(Text::new("Name").width(Length::FillPortion(3)))
.push(Text::new("Email").width(Length::FillPortion(5)));
let users_list = self.user_list.users.iter().fold(
Column::new().spacing(10),
|column, user| {
column.push(
Row::new()
.spacing(10)
.push(Text::new(user.id.to_string()).width(Length::FillPortion(1)))
.push(Text::new(&user.name).width(Length::FillPortion(3)))
.push(Text::new(&user.email).width(Length::FillPortion(5)))
)
},
);
let scrollable_users = Scrollable::new(&mut self.user_list.scroll)
.push(users_list)
.height(Length::Fill);
let content = Column::new()
.spacing(20)
.push(title)
.push(form_row)
.push(header_row)
.push(scrollable_users);
Container::new(content)
.width(Length::Fill)
.height(Length::Fill)
.padding(20)
.into()
}
}
fn main() -> iced::Result {
UserManager::new().run(Settings::default())
}
impl iced::Application for UserManager {
type Executor = iced::executor::Default;
type Message = Message;
type Flags = ();
fn new(_flags: ()) -> (Self, iced::Command<Message>) {
(Self::new(), iced::Command::none())
}
fn title(&self) -> String {
String::from("User Manager - Iced")
}
fn update(&mut self, message: Message) -> iced::Command<Message> {
self.update(message);
iced::Command::none()
}
fn view(&mut self) -> Element<Message> {
self.view()
}
}
Mobile Application Development Link to heading
Enterprise applications increasingly require mobile interfaces. While Rust is not yet a mainstream language for mobile development, there are several approaches to building mobile apps with Rust.
React Native with Rust Native Modules Link to heading
You can use Rust to build native modules for React Native applications:
// rust/src/lib.rs
use jni::JNIEnv;
use jni::objects::{JClass, JString};
use jni::sys::jstring;
#[no_mangle]
pub extern "C" fn Java_com_example_RustModule_processData(
env: JNIEnv,
_class: JClass,
input: JString,
) -> jstring {
// Convert Java string to Rust string
let input: String = env
.get_string(input)
.expect("Couldn't get Java string!")
.into();
// Process the data
let output = format!("Processed: {}", input);
// Convert Rust string back to Java string
let output = env
.new_string(output)
.expect("Couldn't create Java string!");
output.into_inner()
}
JavaScript bridge:
// RustModule.js
import { NativeModules } from 'react-native';
const { RustModule } = NativeModules;
export default {
processData: (input) => RustModule.processData(input),
};
React Native component:
import React, { useState } from 'react';
import { View, TextInput, Button, Text } from 'react-native';
import RustModule from './RustModule';
const App = () => {
const [input, setInput] = useState('');
const [output, setOutput] = useState('');
const handleProcess = async () => {
const result = await RustModule.processData(input);
setOutput(result);
};
return (
<View style={{ padding: 20 }}>
<TextInput
value={input}
onChangeText={setInput}
placeholder="Enter data to process"
style={{ borderWidth: 1, padding: 10, marginBottom: 10 }}
/>
<Button title="Process with Rust" onPress={handleProcess} />
{output ? (
<Text style={{ marginTop: 20 }}>Result: {output}</Text>
) : null}
</View>
);
};
export default App;
Flutter with Rust FFI Link to heading
You can integrate Rust with Flutter using FFI:
// rust/src/lib.rs
use std::os::raw::{c_char};
use std::ffi::{CString, CStr};
#[no_mangle]
pub extern "C" fn process_data(input: *const c_char) -> *mut c_char {
let c_str = unsafe {
assert!(!input.is_null());
CStr::from_ptr(input)
};
let input_str = c_str.to_str().unwrap();
let output = format!("Processed: {}", input_str);
let c_string = CString::new(output).unwrap();
c_string.into_raw()
}
#[no_mangle]
pub extern "C" fn free_string(s: *mut c_char) {
unsafe {
if s.is_null() { return }
CString::from_raw(s)
};
}
Dart FFI:
// lib/rust_bridge.dart
import 'dart:ffi';
import 'dart:io';
import 'package:ffi/ffi.dart';
// Load the dynamic library
final DynamicLibrary rustLib = Platform.isAndroid
? DynamicLibrary.open("librust.so")
: DynamicLibrary.process();
// Define the function signatures
typedef ProcessDataNative = Pointer<Utf8> Function(Pointer<Utf8>);
typedef ProcessData = Pointer<Utf8> Function(Pointer<Utf8>);
typedef FreeStringNative = Void Function(Pointer<Utf8>);
typedef FreeString = void Function(Pointer<Utf8>);
// Create the Dart functions that call into native code
final ProcessData _processData = rustLib
.lookup<NativeFunction<ProcessDataNative>>('process_data')
.asFunction();
final FreeString _freeString = rustLib
.lookup<NativeFunction<FreeStringNative>>('free_string')
.asFunction();
// Wrapper function to handle memory management
String processData(String input) {
final inputPointer = input.toNativeUtf8();
final resultPointer = _processData(inputPointer);
final result = resultPointer.toDartString();
// Free the memory
malloc.free(inputPointer);
_freeString(resultPointer);
return result;
}
Flutter widget:
// lib/main.dart
import 'package:flutter/material.dart';
import 'rust_bridge.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Rust FFI Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final TextEditingController _controller = TextEditingController();
String _output = '';
void _processData() {
final input = _controller.text;
if (input.isNotEmpty) {
final result = processData(input);
setState(() {
_output = result;
});
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Rust FFI Demo'),
),
body: Padding(
padding: EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
TextField(
controller: _controller,
decoration: InputDecoration(
labelText: 'Enter data to process',
border: OutlineInputBorder(),
),
),
SizedBox(height: 16),
ElevatedButton(
onPressed: _processData,
child: Text('Process with Rust'),
),
SizedBox(height: 16),
if (_output.isNotEmpty)
Text(
'Result: $_output',
style: TextStyle(fontSize: 16),
),
],
),
),
);
}
}
Conclusion Link to heading
Rust provides a rich ecosystem for developing enterprise applications across various platforms:
- Web Applications: From server-side rendering with templates to WebAssembly frontends with Yew or Dioxus
- CLI Applications: Robust command-line tools with Clap, interactive interfaces with Dialoguer, and progress visualization with Indicatif
- Desktop Applications: Cross-platform GUIs with Tauri (web technologies) or Iced (native Rust)
- Mobile Applications: Integration with React Native or Flutter through native modules and FFI
When choosing an approach for your enterprise application:
- Consider your team’s expertise: Web technologies may be more familiar to your team than native GUI frameworks
- Evaluate performance requirements: Native Rust UIs generally offer better performance than WebView-based approaches
- Assess cross-platform needs: Some frameworks offer better cross-platform support than others
- Factor in integration requirements: Enterprise applications often need to integrate with existing systems
In the next post, we’ll explore miscellaneous tools for Rust enterprise applications, focusing on observability, logging, and machine learning integration.
Stay tuned!