Singleton

The Singleton pattern is a creational design pattern that ensures a class has only one instance and provides a global point of access to it. It is useful when you need to restrict the instantiation of a class to a single object and provide a single point of access to a resource.

Implementation in Rust

A pure safe way to implement Singleton in Rust is using no global variables at all and passing everything around through function arguments. The oldest living variable is an object created at the start of the main()

fn change(global_state: &mut u32) {
    *global_state += 1;
}

fn main() {
    let mut global_state = 0u32;

    change(&mut global_state);
    println!("Final state: {}", global_state);

    change(&mut global_state);
    println!("Final state: {}", global_state);
}

Using mutex

Starting with Rust 1.63, it can be easier to work with global mutable singletons, although it's still preferable to avoid global variables in most cases.

Now that Mutex::new is const, you can use global static Mutex locks without needing lazy initialization.

use std::sync::Mutex;

static ARRAY: Mutex<Vec<i32>> = Mutex::new(Vec::new());

fn do_a_call() {
    ARRAY.lock().unwrap().push(1);
}

fn main() {
    do_a_call();
    do_a_call();
    do_a_call();

    println!("Called {} times", ARRAY.lock().unwrap().len());
}

Implementation in Go

Using sync.Once

Go's sync.Once package offers a thread-safe method to initialize a value precisely once. This feature is perfect for implementing the Singleton pattern, eliminating the need for explicit locking mechanisms.

package singleton

import "sync"

type singleton struct{}

var instance *singleton
var once sync.Once

func GetInstance() *singleton {
    once.Do(func() {
        instance = &singleton{}
    })
    return instance
}

Using mutex

The sync.Mutex provides a locking mechanism to ensure that only one goroutine can access the critical section of code at a time, which is useful for creating and managing the singleton instance safely.

package singleton

import (
    "fmt"
    "sync"
)

type singleton struct {}

var instance *singleton
var lock = &sync.Mutex{}

func getInstance() *singleton {
    if instance == nil {
        // Only one goroutine at a time can go next
        lock.Lock()
        defer lock.Unlock()

        if instance == nil {
            fmt.Println("Instance created")
            instance = &singleton{}
        } else {
            fmt.Println("Instance exists")
        }
    } else {
        fmt.Println("Instance exists")
    }
    return instance
}