NotDefine.dev

Defining the Undefined. Exploring system design, performance, and memory safety with Go and Rust.

  • void *notdefine; // Why this blog?

    notdefine.dev is a play on my name, Nicola De Filippo. It also fits: most of what I write about lives close to undefined behavior.

    This is an engineering blog. No hype, no AI trends. Deep dives into Rust and Go, internals, concurrency, performance, system design.

  • Why wrapping a Receiver in Arc and Mutex is a code smell in async Rust

    We can start from a real problem: how to fetch N urls? The best ideas are the simplest, so create a worker pool in rust and distribute the job in the queue. There are multiple ways to do it. Not all of them are right. In this post we’ll see two approaches, and we’ll see why one is wrong.

    (more…)
  • Sharing state across threads: from Arc to clean Rust

    In the previous post each thread had its own map and we merged them at the end. That works, but what if the threads need to write to the same data structure? You need shared state and that changes everything.
    If thread::scope or &’static str are unfamiliar, the previous post covers both.

    Shared state means one map, multiple writers. Here is the simplest way to make that work.

    (more…)
  • Rust threads: why scope gives you more than spawn

    The goal of this post is to take a look at threads in Rust, really I split the topic in two posts (a full understanding requires more).
    To explain the problem let’s start with a simple problem, a log message type counter in a file; nothing special but it’s enough to illustrate the concept.

    (more…)
  • into_inner in Rust

    A pattern used in Rust, mainly in the standard library, is into_inner. What does this pattern allow? When is it used?

    Imagine having a struct that wraps some data, for example, a map. You can interact with it through methods defined on the struct, which can enforce validation or limit concurrent access. But at some point you no longer need any of that, you just need the raw data. That’s when you can decide to unwrap it and use it directly, bypassing the struct’s methods entirely.

    (more…)
  • Golang in twenty minutes

    In this first post of the series “Golang in Twenty Minutes”, we will see the fundamentals of the Go language. I assume that the reader already knows at least one programming language.

    A variable can be declared in two ways:

    var name string
    name = "Bob"
    
    lastName := "Smith"

    Thus, we can declare a variable using the var keyword followed by a variable name and its type. The short form allows you to declare and assign a variable in a single step by setting its value; the variable then gets its type from the value assigned during initialization.

    Like variables, constants exist, but their value cannot be changed once assigned.

    const Pi float64 = 64
    const G = 9.18

    In this case, the keyword is const, followed by the constant name, the type, and the value. Alternatively, using the short form, we can omit the type, which is inferred from the initialization.

    (more…)
  • WaitGroup.Go() 

    At the moment, Go 1.26 is in RC, but before looking at the new features, it may be useful to review something that was introduced in Go 1.25.

    In this post, we look at WaitGroup.Go(). The purpose of this function is to create a goroutine and automatically increment the counter used by the WaitGroup to track when all goroutines have completed.

    Let’s see an example using what we had in version 1.24:

    package main
    
    import (
    	"fmt"
    	"sync"
    	"time"
    )
    
    func main() {
    	var wg sync.WaitGroup
    
    	for i := 0; i < 10; i++ {
    		wg.Add(1)
    		go func() {
    			defer wg.Done()
    			time.Sleep(1000 * time.Millisecond)
    			fmt.Printf("Goroutine %d \n", i)
    		}()
    	}
    	wg.Wait()
    	fmt.Println("All goroutines completed")
    }
    (more…)