Tuesday, 6 October 2026

Fire and forget - Try catch and the importance of awaiting async methods

Async void methods are sometimes used in fire and forget scenarios and in event handlers in application code that uses frameworks such as Windows Forms or WPF. Consider the following C# code :


async Task Main()
{
	try
	{
		Operations.TidyCache("");		
		Console.WriteLine("Completed Successfully");
	}
	catch (Exception ex)
	{
		Console.Error.WriteLine($"Critical error: {ex.Message}"); //this try-catch will not be entered due to lack of awaiting Operations.TidyCache
	}	                                                                                                                                                                                                                                                                                                                                                                                                                         
}

public static class Operations {
	
	
	public static async void TidyCache(string key){
		
		ArgumentException.ThrowIfNullOrEmpty(key);
		await Task.Run(async() =>
		{
			await System.Threading.Tasks.Task.Delay(500);
			Console.WriteLine("Finished running async task : waited half a second above");
		});
		
		
	}
}


Output from code above :
Completed Successfully ArgumentException (...) The value cannot be an empty string. (Parameter 'key')


The problem with the code shown above is that although we do an initial error of passing in an empty string as the parameter key to method TidyCache, our try-catch will not be actually catched. The reason for this is that our async method is called and entered and the ArgumentException is thrown inside the async state machin and therefore thrown outside the try/catch. The end result is that since we do not await the method either, the caller will not get the chance to catch the exception and the exception is thrown directly on the thread pool and can also in some applications lead to application crash. We can move the try catch inside the TidyCache method and let the async method return Task instead of void. The calling code uses a discard assignment.

First solution



async Task Main()
{
	try
	{
		_ =Operations.TidyCache("");
	
		Console.WriteLine("Completed Successfully");
	}
	catch (Exception ex)
	{
		Console.Error.WriteLine($"Critical error: {ex.Message}"); //this try-catch will not be entered due to lack of awaiting Operations.TidyCache
	}
}

public static class Operations
{

	public static async Task TidyCache(string key)
	{
		try
		{
			ArgumentException.ThrowIfNullOrEmpty(key);
			await Task.Delay(500);
			Console.WriteLine("Finished running async task");
		}
		catch (Exception ex)
		{
			Console.Error.WriteLine($"TidyCache failed: {ex.Message}");
		}
	}

}


Output from the code above :
TidyCache failed: The value cannot be an empty string. (Parameter 'key') Completed Successfully

Second solution

The first solution is the correct pattern for fire and forget pattern. The second solution shown below is the right way to handle exception for async methods by allowing the async task to flow its exception up to the caller and actually await the async method.


async Task Main()
{
	try
	{
		await Operations.TidyCache("");
		Console.WriteLine("Completed Successfully");
	}
	catch (Exception ex)
	{
		Console.Error.WriteLine($"Critical error: {ex.Message}"); //this try-catch will not be entered due to lack of awaiting Operations.TidyCache
	}	                                                                                                                                                                                                                                                                                                                                                                                                                                                   
}

public static class Operations {	
	
	public static async Task TidyCache(string key){
		
		ArgumentException.ThrowIfNullOrEmpty(key);
		await Task.Run(async() =>
		{
			await System.Threading.Tasks.Task.Delay(500);
			Console.WriteLine("Finished running async task : waited half a second above");
		});			
	}
	
}


Output from code above :
Critical error: The value cannot be an empty string. (Parameter 'key')


The second solution shows the behavior and execution flow most programmers will expect from the code. The exception inside the async method that is thrown is catched in the calling code. Finally some (block) quotes about async void methods:

“async void is where exceptions go to die.”

“async void is the ghost of a Task — it haunts your runtime.”

“If you think you need async void, you probably need therapy.”

“async void is like fire-and-forget — except you forget the fire.”

“Use async void when you want exceptions to bypass your try/catch like a VIP.”

Wednesday, 16 September 2026

Creating 2d scatter plots with Rust and Plotly

This article shows how 2d scatter graphs can be created using Rust and Plotly crate. The following screenshot shows generated 2d scatter graph. Plotly is very powerful graph library with a lot of graphs supporting 2d and 3d data visualization. We can use Plotly to generate the plots / graphs / diagrams into rasterized pictures, default format is PNG format. In the demo, the plot is displayed using a html file and the Plotly Javascript - Js - library. The html file shows controls for exporting the Plotly plot / graph / diagram to a picture. Default format is again PNG in image export. You can use Plotly and Rust to generate pictures or HTML in the background and display them in a web browser or any other client supporting graphical display of plots or pictures.
Plotly is a data visualization library that enables users to create interactive, publication ready charts and dashboards in Python, R and JavaScript. It is widely used for exploratory data analysis, business reporting and web‑based visualisations.
The Plotly website is available at the following url:

https://plotly.com/python/



Github repo with source code for the demo

My Github repo with the demo source code in this article is available here:
https://github.com/toreaurstadboss/RustWithPlotly2dDemo

Screenshot of the demo
Shown below is a screenshot running the demo. A scatter graph with 3 data series are shown. A color scale with palette Viridis is shown. The color scale also controls the color of the points depending on the point's y value.



Setting up the Plotly crate

Cargo add Plotly adds the Plotly crate. Your Rust application can then star using Plotly. Please note that Plotly crate can let you output pictures, which then can be shown in a console application or a web site for example.

Rust manifest file

The Rust manifest file - that is Cargo.toml file - looks like this :
Cargo.toml

[package]
name = "plotly_plotdemo2d"
version = "0.1.0"
edition = "2026"

[dependencies]
plotly = "0.14.1"
rand = "0.10.2"





Helper methods for creating the 2d scatter plot

The following helper methods sets up shared look of axis and series (called 'traces' in Plotly) font sizes and injects HTML script for a shuffle button to regenerate 2d scatter plots with 3 series with randomized y-values. Also, setting up page background style and overall container layout in the HTML is also shown below.
main.rs


fn make_scatter_series(series_name: &str) -> Box<Scatter<i32, i32>> {

    let xpoints: Vec<i32> = (1..=10).collect();
    let ypoints: Vec<i32> = (1..=10)
        .map(|_| rand::random_range(-80..=80))
        .collect();

    let serieslabels: Vec<String> = xpoints
        .iter()
        .zip(&ypoints)
        .map(|(x, y)| format!("({}, {})", x, y))
        .collect();
    let seriesmarker = series_marker(series_name == "Series 1", &ypoints);

     let seriestrace = Scatter::new(xpoints, ypoints)
        .mode(Mode::LinesMarkersText)
        .name(series_name)
        .text_array(serieslabels)
        .text_position(Position::TopCenter)
        .text_font(trace_font())
        .hover_template("(%{x}, %{y})<extra></extra>")
        .marker(seriesmarker);

     seriestrace
}

/* Helper methods for drawing the 2d scatter plot */

fn axis_font() -> Font {
    Font::new()
        .family("Aptos, Segoe UI, Arial, sans-serif")
        .size(16)
        .color("#F8FAFC")
}

fn trace_font() -> Font {
    Font::new()
        .family("Aptos, Segoe UI, Arial, sans-serif")
        .size(11)
        .color("#E2E8F0")
}

fn series_marker(show_scale: bool, ypoints: &[i32]) -> Marker {
    let marker = Marker::new()
        .size(10)
        .color_array(ypoints.to_vec())
        .color_scale(ColorScale::Palette(ColorScalePalette::Viridis))
        .show_scale(show_scale);

    if show_scale {
        marker.color_bar(ColorBar::new().title("Intensity (Y value)"))
    } else {
        marker
    }
}

fn apply_page_background(output_file: &PathBuf) {
    let html = fs::read_to_string(output_file).expect("failed to read generated Plotly HTML");
    let styled_html = html
        .replacen("height:100%; width:100%;", "height:100%; width:50%; margin:0 auto;", 1)
        .replacen(
        "<body>",
        &format!(r#"<body style="{}">"#, PAGE_BACKGROUND_STYLE),
        1,
    );

    fs::write(output_file, styled_html).expect("failed to rewrite Plotly HTML with page styling");
}

fn inject_shuffle_button_and_script(output_file: &PathBuf) {
    let cargo_root = env!("CARGO_MANIFEST_DIR");
    let shuffle_block = std::fs::read_to_string(Path::new(cargo_root).join("assets/shuffle_2dscatter_plot.html")).expect("failed to read shuffle block HTML");

    let html = fs::read_to_string(output_file).expect("failed to read generated Plotly HTML");
    let updated_html = html.replacen("</body>", &format!("{}\n</body>", shuffle_block), 1);

    fs::write(output_file, updated_html).expect("failed to inject shuffle button and script");
}




Main program

The plot is written to html using the plotly::Plot method write_html, this writes the plot into a html file with script references to the Plotly Js library and SVG export Js lib. The code checks if target OS is windows and if so, spawns a new process using std::Command::new("cmd") with arguments pased in to start the file. This will show a dialog box of which browser to open the file and if that is already configured, opens the generated html file into the configured web browser to handle html files in the OS.
main.rs



use plotly::{
    common::{Anchor, ColorBar, ColorScale, ColorScalePalette, Font, Marker, Mode, Orientation, Position, Title},
    layout::{Axis, Legend},
    Layout, Plot, Scatter,
};
use std::{fs, path::PathBuf, path::Path, process::Command};

const DTICK_SIZE: f64 = 10.0;
const DTICK_SIZE_X_AXIS: f64 = 1.0;
const OUTPUT_FILE_NAME: &str = "2dscatter.html";
const PAGE_BACKGROUND_STYLE: &str =
    r#"margin:0; min-height:100vh; background: linear-gradient(135deg, #081120 0%, #132238 45%, #050816 100%); font-family: 'Aptos', 'Segoe UI', Arial, sans-serif;"#;


fn main() {
    println!("Generating a 2d scatter plot using Plotly and Rust - Demo");

    let grid_color: &str = "rgba(148, 163, 184, 0.35)";

    let mut plot = Plot::new();

    for i in 1..=3 {
        let series_trace = make_scatter_series(&format!("Series {}", i));
        plot.add_trace(series_trace);
    }

    plot.set_layout(
        Layout::new()
            .title(
                Title::with_text("2D scatter plot with Plotly and Rust").font(
                    Font::new()
                        .family("Aptos, Segoe UI, Arial, sans-serif")
                        .size(24)
                        .color("#F8FAFC")
            ))
            .font(
                Font::new()
                    .family("Aptos, Segoe UI, Arial, sans-serif")
                    .size(14)
                    .color("#E2E8F0"))
            .legend(
                Legend::new()
                    .orientation(Orientation::Horizontal)
                    .x(0.0)
                    .x_anchor(Anchor::Left)
                    .y(-0.22)
                    .y_anchor(Anchor::Top),
            )
            .x_axis(
                Axis::new()
                    .title(Title::with_text("X Axis").font(axis_font()))
                    .dtick(DTICK_SIZE_X_AXIS)
                    .show_grid(true)
                    .grid_color(grid_color))
            .y_axis(
                Axis::new()
                    .title(Title::with_text("Y Axis").font(axis_font()))
                    .dtick(DTICK_SIZE)
                    .show_grid(true)
                    .grid_color(grid_color))
            .paper_background_color("rgba(0, 0, 0, 0)")
            .plot_background_color("rgba(15, 23, 42, 0.82)"));

    let output_file = PathBuf::from(OUTPUT_FILE_NAME);
    plot.write_html(&output_file);
    apply_page_background(&output_file);
    inject_shuffle_button_and_script(&output_file);

    #[cfg(target_os = "windows")]
    {
        println!("Opening 2d scatter plot..");
        let _ = Command::new("cmd")
            .args(["/C", "start", "", output_file.to_str().unwrap()])
            .spawn();
    }

}




Shuffle button html static file

To provide generating sample data, a static html file containing a little bit Javascript is used to provide some dynamic reload capability. Randomized data is generated. We use Javascript and not Rust to regenerate the series for the Plotly plot showing the 3 data series in the scatter x,y plot.
Shuffle_2d_Scatter_plot.html


<button
  id="shuffle-button"
  style="
    position: fixed;
    top: 16px;
    left: 16px;
    z-index: 1000;
    padding: 10px 14px;
    border: 0;
    border-radius: 10px;
    background: #38bdf8;
    color: #081120;
    font:
      600 14px "Aptos",
      "Segoe UI",
      Arial,
      sans-serif;
    cursor: pointer;
    box-shadow: 0 10px 24px rgba(15, 23, 42, 0.35);
  "
>
  Shuffle series
</button>
<script>
  const graphDiv = document.getElementById("plotly-html-element");

  function buildSeries(seriesName, showScale) {
    const xValues = Array.from({ length: 10 }, (_, index) => index + 1);
    const yValues = Array.from(
      { length: 10 },
      () => Math.floor(Math.random() * 161) - 80,
    );

    return {
      x: xValues,
      y: yValues,
      mode: "lines+markers+text",
      name: seriesName,
      text: xValues.map((xValue, index) => `(${xValue}, ${yValues[index]})`),
      textposition: "top center", 
      hovertemplate: "(%{x}, %{y})<extra></extra>",
      textfont: {
        family: "Aptos, Segoe UI, Arial, sans-serif",
        size: 11,
        color: "#E2E8F0",
      },
      marker: {
        size: 10,
        color: yValues,
        colorscale: "Viridis",
        showscale: showScale,
        colorbar : {
            title: {
                text: "Intensity (Y-value)",
            }
        },
      },
    };
  }

  function shuffleSeries() {
    Plotly.react(
      graphDiv,
      [
        buildSeries("Series 1", true),
        buildSeries("Series 2", false),
        buildSeries("Series 3", false),
      ],
      graphDiv.layout,
    );
  }

  document
    .getElementById("shuffle-button")
    .addEventListener("click", shuffleSeries); 
</script>



StackBlitz demo running the Rust+Plotly demo

A StackBlitz demo is available online here that shows how we can build the Rust code into Wasm Web Assembly and use Vite to display the demo.

https://stackblitz.com/~/github.com/toreaurstadboss/rust-wasm-plotly?file=rust/src/scatter_demo.rs

The Github repo for the StackBlitz based demo is here:

https://github.com/toreaurstadboss/rust-wasm-plotly

Screenshot of StackBlitz running demo :

Monday, 14 September 2026

Traits in Rust - Adding behavior to types in Rust

In C#, we have extension methods to add behavior and functionality to existing types. This is done without changing the existing type definition. In Rust, we have traits to add behavior. In Rust, traits are core language feature and a more fundamental abstraction mechanism. It is closer to a mix of interfaces, generic contraints and extension methods. Let's look at how we can add behavior in Rust using traits. I have added the source code in this article in my Github repo here:

➡️ https://github.com/toreaurstadboss/RustLinqDemo1

The API-docs for the traits methods implemented in my small hobby lib of Linq-like Rust traits with also source code is available in the following

RustDoc



➡️ https://toreaurstadboss.github.io/RustLinqDemo1/docs/doc/rustlinqdemo1/sequence_extensions/index.html

Let's look at making some methods similar to C# and the extension methods of LINQ as a good example how to use traits in Rust. We are going to use either a slice type , which is written as [T] or a vector. This will be similar to code that uses to IEnumerable of T in C#. Let's first define a Skip method and a Take method using traits.

sequence_extensions.rs



/// Returns an owned slice of the start of a vector.
/// Provides owned start of the provided vector.
pub trait TakeOwned {
    type Item;

    /// Returns up to n items from the start of the vector.
    fn take_owned(self, n: usize) -> Vec<Self::Item>;
}

impl<T> TakeOwned for Vec<T> {
    type Item = T;

    fn take_owned(self, n: usize) -> Vec<Self::Item> {
        let len = self.len();
        self.into_iter().take(n.min(len)).collect()
    }
}

// Provides skipped slice by n items that returns the rest of the vector
pub trait SkipOwned {
    type Item;

    fn skip_owned(self, n: usize) -> Vec<Self::Item>;
}

impl<T> SkipOwned for Vec<T> {
    type Item = T;

    fn skip_owned(self, n: usize) -> Vec<Self::Item> {
        self.into_iter().skip(n).collect()
    }
}


As the code above shows, the trait is first defined, conceptually similar to an interface in C# and the impl bit takes care of implementing the trait. As we see, the trait does only define and not implement the logic. This is done in the impl bit. Our trait here is generic defined by type parameter T and is implemented for Vector of type Self::Item which is bound to T via the type argument. Both skip and take methods here are possible to chain, just like in C# Linq methods, it is shown below how to do this in a Rust unit test below.

tests / test.rs



use rustlinqdemo1::sequence_extensions::{
    All, Any, ElementAtOrDefault, FirstOrDefault, LastOrDefault, SkipOwned, SkipTakeOwned,
    TakeOwned, TakeRef,
}; //a  list of traits shown here

#[test]
fn skip_owned_take_owned_chained_returns_expected() {
    let values : Vec<i32> = vec![1, 2, 3, 4, 5, 6, 7];
    assert_eq!(values.skip_owned(3).take_owned(4), vec![4, 5, 6, 7]);
}


Any method here that returns an owned slice of memory is named _owned as suffix as a Rust convention. And any method that borrows slices of memory are named _ref in my little Rust Linq-like lib. A trait could of course combine multiple functionality , not just for example use chained methods. Let's look at a SkipTakeOwned method.

sequence_extensions.rs



/// Provides owned subsequences by consuming a vector.
pub trait SkipTakeOwned {
    type Item;

    /// Returns up to n items after skipping m items.
    fn skip_take_owned(self, m: usize, n: usize) -> Vec<Self::Item>;
}

impl<T> SkipTakeOwned for Vec<T> {
    type Item = T;

    fn skip_take_owned(self, m: usize, n: usize) -> Vec<Self::Item> {
        let len = self.len();
        self.into_iter().skip(m.min(len)).take(n.min(len)).collect()
    }
}



The test looks like the following

tests / test.rs



#[test]
fn skip_take_owned_returns_requested_range() {
    let values = vec![1, 2, 3, 4, 5];

    assert_eq!(values.skip_take_owned(1, 2), vec![2, 3]);
}


A more complex example of a Rust trait is using additional libs to make a group by method.

sequence_extensions.rs



use std::collections::HashMap;

use itertools::Itertools;

/// Groups items into owned vectors keyed by a selector result.
pub trait GroupByOwned {
    type Item;

    /// Consumes the vector and groups its items by `key_selector`.
    fn group_by_owned<F, K>(self, key_selector: F) -> HashMap<K, Vec<Self::Item>>
    where
        F: FnMut(&Self::Item) -> K,
        K: std::hash::Hash + Eq;
}

impl<T> GroupByOwned for Vec<T> {
    type Item = T;

    fn group_by_owned<F, K>(self, key_selector: F) -> std::collections::HashMap<K, Vec<T>>
    where
        F: FnMut(&T) -> K,
        K: std::hash::Hash + Eq,
    {
        self.into_iter().into_group_map_by(key_selector)
    }
}


To use the itertools, add the following Crate into your `cargo.toml` file:

Cargo.toml



[dependencies]
itertools = "0.15.0"


For C# developers, Crates are library packages, just another package format as Nuget in .NET or Npm in NodeJs. The test below show how to test the GroupByOwned trait implementation in a unit test.

tests / test.rs



#[cfg(test)]
#[derive(Debug, Clone)]
struct User {
    id: i32,
    name: String,
}

#[cfg(test)]
fn sample_users() -> Vec<User> {
    vec![
        User {
            id: 1,
            name: "Alice".to_string(),
        },
        User {
            id: 2,
            name: "Bob".to_string(),
        },
        User {
            id: 3,
            name: "Bob".to_string(),
        },
    ]
}

  #[test]
    fn groupby_returns_expected_count() {
        let users = sample_users();
        let grouped_users = users.group_by_owned(|user| user.name.clone());

        assert_eq!(grouped_users.len(), 2);
        assert_eq!(grouped_users.get("Alice").map(Vec::len), Some(1));
        assert_eq!(grouped_users.get("Bob").map(Vec::len), Some(2));
    }