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?

7
  • What are you trying to achieve by doing that? Commented Aug 22, 2020 at 5:04
  • @Herohtar I am working on a library that uses unicode box characters 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. Commented Aug 22, 2020 at 5: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? Commented Aug 22, 2020 at 5:37
  • 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. Commented Aug 22, 2020 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 Examples 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 Examples, 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. Commented Aug 22, 2020 at 13:50

0

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.