4

I would like to make a struct whose constructor can only be called once. For example:

pub struct Example;
impl Example {
    pub fn new() -> Self {
         // Do stuff
    }
}

fn main() {
    Example::new() // This should work fine
    Example::new() // This should panic
}

The only way I've figured out so far is to use a static mut: bool and make the constructor unsafe:

static mut CONSTRUCTED: bool = false;
pub struct Example;
impl Example {
    pub unsafe fn new() {
        if CONSTRUCTED {
            panic!("You many only construct Example once");
        }
        else {
            CONSTRUCTED = true;
        }
    }
}

While this is fine, I would like to find a solution that doesn't use unsafe rust, or at least doesn't force someone the Example struct to use unsafe rust. Maybe it could be done using the FnOnce trait?

To reiterate, my question is this: Can I make a public function in safe rust that can only be called once, and, if so, how?

Liam Bloom
  • 164
  • 2
  • 10
  • What are you trying to achieve by doing that? – Herohtar Aug 22 '20 at 05:04
  • @Herohtar I am working on a library that uses [unicode box characters](https://en.wikipedia.org/wiki/Box-drawing_character#Unicode) to draw stuff to stdout, and I have a parent element that clears the entire console and redraws when something changes (like if the terminal window is resized or the drawing is changed). If there's more than one of these parent elements, then it will start trying to draw two things to the console at once, which is undesirable. – Liam Bloom Aug 22 '20 at 05:13
  • 3
    Making a constructor that panics on subsequent calls seems like a bad idea. The fact that creating more than one instance of a struct causes bad behavior seems like a design problem that should be fixed instead. But whatever the case, it sounds like what you're asking for is a singleton: [How can you make a safe static singleton in Rust?](https://stackoverflow.com/questions/27221504/how-can-you-make-a-safe-static-singleton-in-rust) – Herohtar Aug 22 '20 at 05:37
  • Also related: [How do I create a global, mutable singleton?](https://stackoverflow.com/questions/27791532/how-do-i-create-a-global-mutable-singleton) – Herohtar Aug 22 '20 at 05:40
  • Yes, a singleton sounds like exactly what I'm looking for. I can't try it right at the moment, but I will look at those post tomorrow and probably use their solutions. Thank you! – Liam Bloom Aug 22 '20 at 05:56
  • 2
    As others pointed out, you want a singleton, but it's worthwhile to understand _why_ the compiler rejects the code. It is not allowed for one thread to mutate a value while another is accessing it. So if you had two threads calling `new()`, you would incur undefined behavior for mutating/reading `CONSTRUCTED`. A simple way to fix the code as provided is to replace `bool` with `AtomicBool`, which is safe and efficient: [playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=ca5c07e3a8998dbd516bf1f9ab8ae64e). – user4815162342 Aug 22 '20 at 10:16
  • 1
    It's usually smarter to go for dependency injection rather than a singleton. In this case that might mean passing the output stream (e.g. `stdout()`) into `Example::new`. That means it can be possible to create two `Example`s that wrap different output streams. Bear in mind, anything that can write to stdout can mess up your box drawing code just as much as creating two `Example`s, so it's still up to the user not to do anything else with stdout after creating an `Example` from it, but that's the same as your original code. – trent Aug 22 '20 at 13:50

0 Answers0