Ask coding questions

← Back to all posts
Rust: how to make a global function that is only callable once?
xxpertHacker (930)

My goal is to make this program fail to compile, using only the type system:

const pow: fn(f64, f64) -> f64 = f64::powf;

fn main() {
    pow(1.0, 2.0);

    pow(3.0, 4.0);
}

pow can be called endlessly, I don't want that.

I tried the following, but it wasn't working without an implementation of the Copy trait:

const pow: &'static dyn FnOnce(f64, f64) -> f64 = &f64::powf;

fn main() {
    pow(1.0, 2.0); // can't move/copy here

    pow(3.0, 4.0);
}

Since I don't know how to use traits, this failed too:

trait T: (FnOnce(f64, f64) -> f64) + Sized + Copy {}

const pow: &dyn T = &f64::powf;

fn main() {
    pow(1.0, 2.0);

    pow(3.0, 4.0);
}

My real use case is that I have a function which creates a hook that must not be created more than once, and using runtime boolean mutation, checking, and branching could introduce bugs, along with increased code size and unnecessay runtime overhead, where the second call should be prevented at compile-time anyway.


Does anyone know how this could be done?

Answered by 19wintersp (1142) [earned 5 cycles]
View Answer
Comments
hotnewtop
19wintersp (1142)

Once is the closest thing probably.

xxpertHacker (930)

@19wintersp

use std::sync::Once;

const once: Once = Once::new();

fn init() {
    println!("init!");
}

fn main() {
    once.call_once(init);

    once.call_once(init);
}

This gives

init!
init!

I'm thinking that I'll need static booleans and atomics instead.

19wintersp (1142)

@xxpertHacker

use std::sync::Once;

static INIT: Once = Once::new();

fn init() {
	INIT.call_once(|| println!("init!"));
}

fn main() {
	init();
	init();
}

gives

init!

¯\_(ツ)_

Coder100 (18109)

getting singleton vibes here

xxpertHacker (930)

@Coder100 Kind of, like calling the class constructor once, calling this arbitrary function one.

xxpertHacker (930)

@Coder100 So, you have no clue how to do this though?