Friday, September 20, 2024

The Bridge Pattern in Rust...

In modern software design, using composition (via the Bridge pattern) instead of inheritance is often considered a better approach, particularly when working with flexible, scalable systems. The Bridge pattern is a structural design pattern that decouples an abstraction from its implementation, allowing the two to vary independently. This pattern promotes composition by separating the interface from the implementation, which solves many problems associated with inheritance.

Here is the code of Bridge pattern implemented in Rust...

trait Color {
fn new(&self) {
}
fn get_name(&self) -> String;
}

struct Red;
struct Black;

impl Color for Red {
fn new(&self) {
println!("The color is Black");
}

fn get_name(&self) -> String {
"Red".to_string()
}
}

impl Color for Black {
fn new(&self) {
println!("The color is Black");
}

fn get_name(&self) -> String {
"Black".to_string()
}
}

trait Gear {
fn new(&self){
}

fn get_name(&self) -> String;
}

struct AutoGear;
struct ManualGear;

impl Gear for AutoGear {
fn new(&self){
println!("The gear is AutoGear");
}

fn get_name(&self) -> String {
"AutoGear".to_string()
}
}

impl Gear for ManualGear {
fn new(&self){
println!("The gear is Manual Gear");
}

fn get_name(&self) -> String {
"ManualGear".to_string()
}
}

trait Vehicle {

fn set_color(&mut self, color : Box<dyn Color>) {}
fn set_gear (&mut self, gear : Box<dyn Gear>) {}

fn display_attributes(&self) { }


}

#[derive()]
struct SmallCar{
color: Box<dyn Color>,
gear: Box<dyn Gear>,
}
#[derive()]
struct Truck{
color: Box<dyn Color>,
gear: Box<dyn Gear>,
}

impl Vehicle for SmallCar {
fn set_color(&mut self, color : Box<dyn Color>) {
self.color = color;
}
fn set_gear(&mut self, gear : Box<dyn Gear>) {
self.gear = gear;
}

fn display_attributes(&self) {
println!("The vehicle color is {:?}", self.color.get_name());
println!("The vehicle gear is {:?}", self.gear.get_name());
}
}

impl Vehicle for Truck {
fn set_color(&mut self, color : Box<dyn Color>) {
self.color = color;
}
fn set_gear(&mut self, gear : Box<dyn Gear>) {
self.gear = gear;
}

fn display_attributes(&self) {
println!("The vehicle color is {:?}", self.color.get_name());
println!("The vehicle gear is {:?}", self.gear.get_name());
}
}

fn main() {
let color = Box::new(Red);
let gear = Box::new(AutoGear);
let truck = Truck {
color: color, gear:gear,
};
truck.display_attributes();
}

Let me tell you in detail what is there in this example.

Here we have a top-level root class called Vehicle having two attributes - color and gear.

The color may be Red and White whereas the Gear may be Manual and Auto.

Now if we put all of these in a monolithic class hierarchy, as shown in the left diagram - there will be many classes.

So, to do it in a better fashion, we maintain two different inheritance hierarchies namely one for Color and another for Gear. And the Vehicle hierarchy is maintained separately.

So from the vehicle hierarchy, we pick up any color and any type of Gear we want from the Color and Gear inheritance hierarchies respectively as shown in the right-side diagram.

The result is that there will be few classes to maintain.

This is exactly what is explained in the following section.

Look at the UML diagram on the left - that is the class hierarchy without using Bridge Pattern. Look at the number of classes - it's many classes.

Now look at the class diagram at the right- look at how fewer classes can manage the same level of different classes if we use the Bridge Pattern




Using composition with the Bridge pattern is often a superior design choice compared to inheritance. It:

- Avoids the pitfalls of deep class hierarchies.
- Promotes code reuse and flexibility.
- Supports better maintainability and scalability.

By favoring composition over inheritance, we can create more modular, flexible, and maintainable systems, which align with modern software design principles such as SOLID.

Here's my implementation of the Bridge pattern in other languages - C++ and Python...

Thursday, September 19, 2024

My exploration of Rust continues... Mediator Design Pattern in Rust...

 This design pattern lets you reduce chaotic dependencies between objects. The pattern restricts direct communications between the objects and forces them to collaborate only via a mediator object.

The Mediator interface declares a method used by components to notify the mediator about various events. The Mediator may react to these events and triggers specific components to execute different yet specific tasks.

The Base Component provides the basic functionality of storing a mediator's reference inside the component.

Concrete Components implement various functionality. They don't depend on other components.

Let me explain it to you with an example.

Suppose two students Ridit and Ishan take a lot of responsibility in a class. But something bad happens between them, and hence they don't talk to each other. So, here comes the ClassMonitor - as a Mediator between Ishan and Ridit - if Ridit does anything that needs Ishan to do another task, Ridit notifies the Class Monitor, and then the Class Monitor asks Ishan to execute the necessary task.


There are many uses for this design pattern. This design pattern can be found in most of the widget message-passing systems - individual widgets don't pass or communicate with each other - but they communicate through the container window.

For Android engineers - you remember how two fragments talk to each other through the container window.

Enjoy.

Happy learning.

Here's the source code for Mediator Pattern implemented through Rust.

use std::cell::RefCell;
use std::rc::Rc;

trait BaseStudent {
fn do_a(&self);
fn do_b(&self);
fn do_c(&self);
fn do_d(&self);
fn set_class_monitor(&mut self, monitor: Rc<RefCell<dyn ClassMonitar>>);
}

struct Ridit {
monitor: Option<Rc<RefCell<dyn ClassMonitar>>>,
}

struct Ishan {
monitor: Option<Rc<RefCell<dyn ClassMonitar>>>,
}

impl BaseStudent for Ridit {
fn do_a(&self) {
println!("Ridit does task A");
if let Some(ref monitor) = self.monitor {
monitor.borrow().notify("A".to_string());
}
}

fn do_b(&self) {
println!("Ridit does task B");
}

fn do_c(&self) {
println!("Ridit does task C");
}

fn do_d(&self) {
println!("Ridit does task D");
}

fn set_class_monitor(&mut self, monitor: Rc<RefCell<dyn ClassMonitar>>) {
self.monitor = Some(monitor);
}
}

impl BaseStudent for Ishan {
fn do_a(&self) {
println!("Ishan does task A");
}

fn do_b(&self) {
println!("Ishan does task B");
if let Some(ref monitor) = self.monitor {
monitor.borrow().notify("B".to_string());
}
}

fn do_c(&self) {
println!("Ishan does task C");
}

fn do_d(&self) {
println!("Ishan does task D");
}

fn set_class_monitor(&mut self, monitor: Rc<RefCell<dyn ClassMonitar>>) {
self.monitor = Some(monitor);
}
}

trait ClassMonitar {
fn notify(&self, event: String);
}

#[derive(Clone)]
struct ConcreteClassMonitor {
ridit: Option<Rc<RefCell<dyn BaseStudent>>>,
ishan: Option<Rc<RefCell<dyn BaseStudent>>>,
}

impl ClassMonitar for ConcreteClassMonitor {
fn notify(&self, event: String) {
if event == "A" {
println!("ClassMonitor reacting to event A and asking Ishan to do task C");
if let Some(ref ishan) = self.ishan {
ishan.borrow().do_c();
}
}

if event == "B" {
println!("ClassMonitor reacting to event B and asking Ridit to do task D");
if let Some(ref ridit) = self.ridit {
ridit.borrow().do_d();
}
}
}
}

fn main() {
// Create instances of students as trait objects
let ridit: Rc<RefCell<dyn BaseStudent>> = Rc::new(RefCell::new(Ridit { monitor: None }));
let ishan: Rc<RefCell<dyn BaseStudent>> = Rc::new(RefCell::new(Ishan { monitor: None }));

// Create ConcreteClassMonitor as trait object (ClassMonitar)
let class_monitor: Rc<RefCell<dyn ClassMonitar>> = Rc::new(RefCell::new(ConcreteClassMonitor {
ridit: Some(Rc::clone(&ridit)),
ishan: Some(Rc::clone(&ishan)),
}));

// Set the monitor in the students
ridit.borrow_mut().set_class_monitor(Rc::clone(&class_monitor));
ishan.borrow_mut().set_class_monitor(Rc::clone(&class_monitor));

// Test the functionality
ridit.borrow().do_a(); // This will notify the monitor and trigger Ishan to do task C
ishan.borrow().do_b(); // This will notify the monitor and trigger Ridit to do task D
}

Monday, September 16, 2024

My tryst with Rust - through the subject of Design Pattern - implementation of Chain of Responsibility...

 I remember in my earlier phase as a software engineer, how I mapped the MFC's command routing algorithm to Chain of Responsibility - and when finally it became clear, there was immense joy for me.

I always say to my students that we should approach a problem not only from how's point of view but also from why's point of views.

Curiosity is one of the greatest traits for a software engineer - nurture that, grow that - if you want to extract joy from your day-to-day journey as a software engineer.

So here we go.

My today's contribution to the learning community - Chain of Responsibility design pattern using Rust.

The Chain of Responsibility design pattern is a behavioural design pattern that allows a request to pass through a series of handlers. Each handler can either handle the request or pass it to the next handler in the chain. This design pattern promotes loose coupling between the sender and receiver of a request, allowing more flexibility in handling specific actions.

Key Features of the Chain of Responsibility Pattern:

Decoupling: 

The sender of the request doesn't need to know which handler will process the request.

Dynamic Chains: 

You can change the chain of handlers at runtime or configure it based on the application's needs.

Multiple Handlers: 

A request can be processed by a series of handlers in sequence until one of them handles the request, or it can propagate through all of them.

Here's the source code...

#[derive(Clone)]
struct Request{
request_type : String,
}
impl Request{
fn new(request_type : String) -> Self{
Request{
request_type : request_type,
}
}
}

trait Employee {
fn set_successor(&mut self, successor:Box<dyn Employee>);
fn handle_request(&mut self, request: Request) ;
}
struct ProjectManager {
successor: Option<Box<dyn Employee>>,
}

struct TeamLeader {
successor: Option<Box<dyn Employee>>,
}

struct GeneralManager{
successor: Option<Box<dyn Employee>>,
}

struct CTO {
successor: Option<Box<dyn Employee>>,
}

impl Employee for CTO{

fn set_successor(&mut self, successor: Box<dyn Employee>) {
//end of the chain... no successor
}
fn handle_request(&mut self, request : Request) {
println!("I am the CTO... I must handle the request as this is the end of the chain");
println!("The request is handled at the CTO level...");
}
}

impl Employee for GeneralManager {
fn set_successor(&mut self, successor: Box<dyn Employee>) {
self.successor = Some(successor);
}

fn handle_request(&mut self, request : Request) {
if request.request_type == "generalmanager_level" {
println!("Handling the request at the General Manager level");
}
else if let Some(ref mut successor) = self.successor {
println!("I am the general manager...Forwarding the request to CTO...");
successor.handle_request(request);
}
}
}

impl Employee for ProjectManager {
fn set_successor(&mut self, successor: Box<dyn Employee>) {
self.successor = Some(successor);
}

fn handle_request(&mut self, request: Request) {
if request.request_type == "projectmanager_level" {
println!("Handling the request at the Project Manager level");
} else if let Some(ref mut successor) = self.successor {
println!("I am the Project Manager...Forwarding the request to General Manager");
successor.handle_request(request);
}
}
}

impl Employee for TeamLeader {
fn set_successor(&mut self, successor: Box<dyn Employee>) {
self.successor = Some(successor);
}

fn handle_request(&mut self, request : Request) {
if request.request_type == "teamleader_level" {
println!("Handling the request at the team_leader level");
}
else if let Some(ref mut successor) = self.successor {
println!("I am the teamleader....Forwarding the request to Project Manager");
successor.handle_request(request);
}
}
}

fn main() {
let request1 = Request::new("teamleader_level".to_string());
let request2 = Request::new("generalmanager_level".to_string());
let request3 = Request::new("cto_level".to_string());
let mut cto = CTO{successor: None};
let mut gm = GeneralManager{successor: None};
let mut manager = ProjectManager {successor: None};
let mut teamLeader = TeamLeader {successor: None};
gm.set_successor(Box::new(cto));
manager.set_successor(Box::new(gm));
teamLeader.set_successor(Box::new(manager));
teamLeader.handle_request(request3);
}

Enjoy...

Sunday, September 15, 2024

Command Pattern in Rust - absolutely driven by intrinsic motivation...

Rust is a multi paradigm language. It supports Object Oriented Programming model (traits and dynamic binding),  functional programming model (closure, etc) and at the same time straight forward procedural programming model.

It won't force a developer to follow a specific paradigm, rather the developers are free to choose and pick any paradigm they want.

As I have come from a pure Object Oriented world, (C++, Java, Python), my tryst with Rust is mainly through the design patterns of Gang of Four book.

Today, i developed a sample example of Command Pattern using Rust.

Key Components of the Command Pattern:

  1. Command Interface:

    • Defines a common interface with a method (usually called execute) that all concrete commands must implement. This allows different types of commands to be invoked in a uniform way.
  2. Concrete Command:

    • Implements the command interface and defines the actual behavior. It binds the Receiver (the object that knows how to carry out the operation) to an action. The execute method is used to invoke the receiver's method(s).
  3. Invoker:

    • The invoker (e.g., a Waiter in a restaurant analogy) is responsible for triggering the command by calling the execute method. It does not know or care about the specifics of the command, only that it can execute it.
  4. Receiver:

    • The receiver (e.g., a Chef) is the object that performs the actual work. The command will delegate the action to the receiver.

Example Scenario: Restaurant

  • Invoker: The Waiter takes an order from the customer and passes it to the chef.
  • Command: The FoodCommand tells the chef what food to prepare.
  • Receiver: The Chef is responsible for actually preparing the food.

The advantage of the Command Pattern is that the Waiter (invoker) doesn’t need to know the details of how food is prepared. It just knows how to pass the command. You could have different kinds of commands (e.g., DrinkCommand, FoodCommand, DessertCommand), and the invoker can execute them all the same way.

Command Pattern Flow:

  1. The Invoker issues a command.
  2. The Concrete Command executes the command by calling methods on the Receiver.
  3. The Receiver performs the action.
Here is the source code of the command pattern in Rust...

#enjoy...

I must admit - I am yet to achieve the flow in Rust - the learning curve is really steep...

trait Command {
fn execute(&self);
}

//Receiver
struct Chef;

impl Chef {
pub(crate) fn prepare_food(&self) {
println!("Chef is preparing the food...");
}
}

struct FoodCommand{
chef : Chef,
}

impl Command for FoodCommand {
fn execute(&self) {
self.chef.prepare_food();
}
}
//Invoker
struct Waiter {
command : Box<dyn Command>,
}


impl Waiter {
fn new () -> Waiter{
let waiter = Waiter {
command : Box::new (FoodCommand {chef : Chef}),
};
waiter
}

fn pass_command_to_chef(&self){
self.command.execute();
}
}

fn main() {
let chef = Chef;
let food_command = FoodCommand {chef};
let waiter = Waiter {command : Box::new(food_command),};
waiter.pass_command_to_chef();
}

Friday, September 13, 2024

Should you learn Rust NOW? My contribution to the Rust community - State Pattern in Rust...

Key Reasons Why Now Is the Right Time to Learn Rust:

1. Growing Popularity and Industry Adoption

According to the 2024 Stack Overflow Developer Survey, Rust is the most admired programming language, with 83% of developers saying they want to use it again. This is the eighth year in a row that Rust has topped this chart. Major Companies Use Rust: Big tech companies like Microsoft, Amazon, Google, Facebook, Dropbox, and Mozilla use Rust in production systems. Learning Rust can open doors to career opportunities at companies that have adopted the language.

2. Memory Safety Without Garbage Collection

Rust’s borrow checker ensures that you manage memory safely without relying on a garbage collector. This makes Rust an ideal language for systems programming, embedded systems and performance-critical applications. If you want to work on low-level programming without common pitfalls like segmentation faults or data races, Rust is an excellent choice.

3. Concurrency Without Fear

Rust’s ownership model allows you to write highly concurrent programs while ensuring thread safety at compile-time. This is a game-changer for developers working on multi-threaded or parallel applications, and it's a good reason to pick up Rust now as more modern software is built with concurrency in mind.

4.Growing Ecosystem and Libraries

Rust's ecosystem is maturing. The package manager and build tool, Cargo, is widely praised for its ease of use.

5. Community and Support

The Rust community is known for being friendly, helpful, and inclusive. The language is also supported by an active working group and regular updates, making it a language that's constantly evolving but staying stable.

Rust is the right choice for Systems programmers. It is a great alternative to C/C++ for writing system-level code (operating systems, device drivers, embedded systems).

So, here we go...

My contribution to the developers' community - State Design Pattern in Rust.

The State Design Pattern is a behavioral design pattern that allows an object to change its behavior when its internal state changes. In other words, the object behaves differently depending on its current state, and the state transitions are handled internally. This pattern helps avoid complex conditional statements (like if-else or match in Rust) and organizes the code to make it more maintainable.

Here is the source code of the implementation of State Pattern in Rust.

use std::thread;
use std::time::Duration;

struct Mamma {
state: Option<Box<dyn State>>,
}
trait State {
fn wash(self: Box<Self>) -> Box<dyn State>;
fn marinate(self: Box<Self>) -> Box<dyn State>;
fn cook( self : Box<Self>)-> Box<dyn State>;
fn serve(self : Box <Self>) -> Box<dyn State>;
}

impl Mamma {
fn new() -> Mamma {
let mamma = Mamma {
state: Some(Box::new(UncleanedState)),
};
mamma
}

fn startcooking(self) -> () {
self.state.unwrap().wash().marinate().cook().serve();
}
}

struct UncleanedState;
struct CleanedState;

struct MarinatedState;
struct CookState;


impl State for UncleanedState {
fn wash(self: Box<Self>) -> Box<dyn State>{
println!("The Chicken is in Uncleaned state. It's being washed...");
thread::sleep(Duration::from_secs(5)); // Pause for 2 seconds
Box::new(CleanedState{})
}
fn marinate(self: Box<Self>)->Box<dyn State>{
self
}

fn cook(self: Box<Self>)->Box<dyn State>{
self
}

fn serve(self : Box<Self>) ->Box<dyn State>{
self
}

}

impl State for CleanedState {
fn wash(self: Box<Self>) -> Box<dyn State> {
self
}
fn marinate(self: Box<Self>) -> Box<dyn State> {
println!("The chicken is in the Cleanedstate. It's being marinated...");
thread::sleep(Duration::from_secs(5));
Box::new(MarinatedState {})
}

fn cook(self: Box<Self>) -> Box<dyn State> {
self
}

fn serve(self: Box<Self>) -> Box<dyn State> {
self
}
}


impl State for MarinatedState {
fn wash(self: Box<Self>) -> Box<dyn State> {
self
}
fn marinate(self: Box<Self>) -> Box<dyn State> {
self
}

fn cook(self: Box<Self>) -> Box<dyn State> {
println!("The chicken is in the Marinatedstate state. It will be cooked now...");
thread::sleep(Duration::from_secs(5));
Box::new(CookState {})
}

fn serve(self: Box<Self>) -> Box<dyn State> {
self
}
}

impl State for CookState {
fn wash(self: Box<Self>) -> Box<dyn State> {
self
}
fn marinate(self: Box<Self>) -> Box<dyn State> {
self
}

fn cook(self: Box<Self>) -> Box<dyn State> {
self

}

fn serve(self: Box<Self>) -> Box<dyn State> {
println!("The chicken is in the Cookedstate");
println!("This is the last state. I m sure the guests will enjoy the chicken...");
self
}
}

fn main() {
let mamma = Mamma::new();
mamma.startcooking();
}

And here's the video for the output of the above source code.


Sunday, September 8, 2024

Will adoption of Rust for mission critical systems popularise GTK-RS and Linux overall?

 For so many years we have been fed with the infamous FUD that Linux is not good for desktops. However, as more and more organizations adopt Rust for mission-critical systems, I think GTK-RS (for developing cross-platform GUI using Rust) and Linux desktops will see a surge in numbers. At least my gut feeling is saying this.

The inherent memory safety system of Rust and the security system of Linux will make a perfect choice for mission-critical and embedded domains. 

Rust’s adoption in mission-critical systems, many of which might run on Linux, could lead to a resurgence of interest in Linux as a development platform. Developers working on Rust projects might choose Linux for its compatibility, performance, and open-source nature. This could also lead to greater investment in Linux desktop environments, where GTK-RS could play a role.

I am sure in the near future, use of GTK-RS for SCADA system's HMI development is unavoidable. Supervisory Control and Data Acquisition (SCADA) systems are crucial in industries like manufacturing, utilities, and energy management. They monitor and control industrial processes through a combination of hardware and software. SCADA systems require robust, reliable, and high-performance software for their Human-Machine Interface (HMI), and GTK-RS offers several advantages for these applications - the first and foremost among them is safety and security - making the HMI foolproof from being hacked.

Not only from security perspective, but there is a strong reasoning of using GTK-RS in SCADA system for performance. SCADA systems need to process multiple data streams concurrently. Rust’s strengths in concurrency and parallelism ensure that GTK-RS-based applications can handle these demands efficiently, providing a responsive interface even under heavy load.

Keeping this in mind, I was just playing around with GTK-RS, and here's a simple GTK-RS-based application called Skip Counter.



You need GTK 3 to run this...

Here is the source code...

extern crate gtk;
use gtk::prelude::*;
use gtk::{Application, ApplicationWindow, Button, Entry, Label, Box};
use std::rc::Rc;
use std::cell::RefCell;

fn main() {
let application = Application::builder()
.application_id("com.example.skip-counter")
.build();

application.connect_activate(|app| {
// Create a new window
let window = ApplicationWindow::new(app);
window.set_title("Skip Counter");
window.set_default_size(300, 150);

// Create a vertical box to hold the label, entry, and button
let vbox = Box::new(gtk::Orientation::Vertical, 5);
window.set_child(Some(&vbox));

// Create a label to display the counter
let label = Label::new(Some("Counter: 0"));
vbox.pack_start(&label, false, false, 0); // Use pack_start in GTK3

// Create an entry for the skip value
let entry = Entry::new();
entry.set_placeholder_text(Some("Enter skip value"));
vbox.pack_start(&entry, false, false, 0); // Use pack_start in GTK3

// Initial counter value
let counter = Rc::new(RefCell::new(0));

// Create a button to increment the counter
let increment_button = Button::with_label("Increment");
vbox.pack_start(&increment_button, false, false, 0); // Use pack_start in GTK3

// Create a button to increment the counter
let reset_button = Button::with_label("Reset");
vbox.pack_start(&reset_button, false, false, 0); // Use pack_start in GTK3


// Connect button click event
let counter_clone = Rc::clone(&counter);
let label_clone = label.clone();
let entry_clone = entry.clone();
increment_button.connect_clicked(move |_| {
let skip_value = entry_clone.text().parse::<i32>().unwrap_or(1);
*counter_clone.borrow_mut() += skip_value;
label_clone.set_text(&format!("Counter: {}", *counter_clone.borrow()));
});

// Connect the reset button click event
let counter_clone = Rc::clone(&counter);
let label_clone = label.clone();
reset_button.connect_clicked(move |_| {
*counter_clone.borrow_mut() = 0;
label_clone.set_text("Counter: 0");
});
// Show the window
window.show_all();
});

application.run();
}

Thursday, September 5, 2024

My first UI based Rust application - A Skip Counter - created using iced UI library...

It's rightly said that when someone teaches another person, actually two people learn.

Here goes my contribution on Teacher's day.

Happy Teacher's day to all the Gurus of the Universe.

Here we go... 

Rust is primarily known for System programming. However, with growing popularity, eventually may be UI based application will be developed using Rust. Rust's UI libraries are still maturing in comparison to C++ (QT) and other languages, however, it's inbuilt memory safety issue may force global companies to consider Rust for UI based mission critical system like SCADA HMI and similar application areas.

Pros of Using Rust for UI Applications:

  1. Memory Safety: Rust's ownership model and memory safety features make it excellent for avoiding memory-related bugs, which is crucial for large-scale UI applications.
  2. Performance: Rust's performance is comparable to C and C++, which can be beneficial in resource-intensive UI applications.
  3. Concurrency: Rust's built-in concurrency model helps in writing fast and reliable multi-threaded applications, which is useful for interactive UIs.
  4. Cross-Platform: Libraries like Iced offer cross-platform capabilities, so you can build desktop applications for different operating systems.

Cons of Using Rust for UI Applications:

  1. Immature Ecosystem: Although libraries like Iced, and Slint are promising, they are still not as mature or feature-rich as UI toolkits available in other languages (e.g., Qt for C++, Flutter for Dart).
  2. Steep Learning Curve: The Rust programming model, particularly its strict borrow checker and ownership system, may pose a challenge for developers who are new to the language, making rapid UI prototyping slower.
  3. Fewer Libraries and Frameworks: While libraries like Iced, GTK exist for Rust, they lack the polish and comprehensive documentation of more established UI frameworks. Integrating features like animations or sophisticated layouts can be more difficult.
Yesterday I was developing a small UI based application called Skip Counter using the iced library for Rust. Here's what it looks like.



And here we go... the source code for this simple application.

use iced::{widget::{Button, Column, Text, TextInput},
Sandbox, Settings, Application, Command, executor,
Theme};
#[derive(Debug, Clone)]
enum Message {
SkipValueChanged(String),
Increment,
Reset,
}

struct SkipCounter {
count: i32,
skip_value: i32,
input_value: String,
}

impl SkipCounter {
fn new() -> Self {
SkipCounter {
count: 0,
skip_value: 1, // Default skip value
input_value: String::new(),
}
}
}
impl Application for SkipCounter {

type Executor = executor::Default; // Here you specify the type for Executor
type Flags = (); // Replace with your actual flags type
type Message = Message;

fn new(_flags: Self::Flags) -> (Self, Command<Self::Message>) {
(SkipCounter::new(), Command::none())
}

fn title(&self) -> String {
String::from("Skip Counter")
}

fn update(&mut self, message: Message) -> iced::Command<Message> {
match message {
Message::SkipValueChanged(value) => {
self.input_value = value.clone();
self.skip_value = value.parse::<i32>().unwrap_or(1);
Command::none()
}
Message::Increment => {
self.count += self.skip_value;
Command::none()
}
Message::Reset => {
self.count = 0;
Command::none()
}
}
}

fn view(&self) -> iced::Element<Message> {
let input = TextInput::new(
"Enter skip value",
&self.input_value,
//Message::SkipValueChanged
).on_input(Message::SkipValueChanged);

let increment_button = Button::new(Text::new("Increment"))
.on_press(Message::Increment);

let reset_button = Button::new(Text::new("Reset"))
.on_press(Message::Reset);

let count_text = Text::new(format!("Current Count: {}", self.count));

Column::new()
.padding(100)
.align_items(iced::Alignment::Center)
.push(input)
.push(increment_button)
.push(reset_button)
.push(count_text)
.into()
}

type Theme = iced::Theme;
}

fn main() -> iced::Result {
SkipCounter::run(iced::Settings::default())
}
 You must have got the point - Rust UI toolkits are still maturing. It will take time, but I am sure it will flourish in the near future.