Store dyn Trait as a static mut

I know I'm probably pushing Rust's statics support to its limits, but I've been stumped on this for quite a while. So far, my project looks like this:

// Crate A
struct Bar {
    // Fields go here blah blah
    field1: i32,
}
impl Bar {
    pub fn new() -> Self {
        Self {
            field1: 50,
            // Fields go here, blah blah
        }
    }
    pub fn set_bar_instance(&mut self) {
        set_bar_instance(self);
    }
}
impl Foo for Bar {
    fn test(&mut self, arg: i32) {
        println!("field1: {} ", self.field1); // "field1: 50"
        self.field1 += 1;
    }
}

// Crate B
trait Foo {
    fn test(&mut self, arg: i32);
}

static mut BAR_INSTANCE: /* what goes here? */; // Errors
pub fn set_bar_instance(ref: /* what goes here? */) {
    unsafe {
        BAR_INSTANCE = /* what goes here */
    };
}
pub fn call_bar_test() {
    unsafe { BAR.test() }; // or something like this
}

The code using the statics is purely single-threaded. There's no way to get around this either: I use statics and raw pointers extensively in one particular part of my program to speed it up (at the expense of safety), and BAR_INSTANCE is my way of bridging the unsafe world to the safer, Rust-friendly world.

If that code example is confusing, here's a run-down: I have a trait called Foo and a struct named Bar that implements it. I would like to pass in an instance of Bar and store it in BAR_INSTANCE somehow, so that call_bar_test can use it. call_bar_test would, in turn, print "here 50" and so on.

I've been trying to store &mut self into BAR_INSTANCE in various ways. I've tried various combinations of Boxes and raw pointers and references, and none of it's worked. Do any of you know how to do this? Thanks in advance.

The easiest solution would be to use boxed trait objects:

static mut BAR_INSTANCE: Option<Box<dyn Foo>> = None;

pub fn set_bar_instance<T: Foo + 'static>(foo: T) {
    unsafe {
        BAR_INSTANCE = Some(Box::new(foo));
    }
}

pub fn call_bar_test(arg: i32) {
    unsafe { BAR_INSTANCE.as_mut().unwrap().test(arg) }
}

(Complete code on Playground)

1 Like

Thanks! That was the number one roadblock while writing my program.

Is there any way to do this without the Option? This is a function that's called frequently and in many places. But I guess that in the grand scheme of things, one conditional branch won't affect performance that much.

Also, is there any way to do this with a &mut self reference, instead of a self reference? In the attached playground link, set_bar_instance uses self, while I'd really like to just use &mut self, since that plays better with the borrow checker. Right now, the compiler is telling me to produce a copy of it, which I definitely don't want to do.

You could instead store a raw pointer, initialized to null. In set_bar_instance, you could use Box::into_raw to convert a box to a raw pointer. You'd need some way to guarantee that it always gets set before anyone tries to dereference it.

You cannot store an &mut reference in a static. You could use an &'static dyn Bar reference, but then you would need to use internal mutability for any mutable fields. Also, the reference would need to be to a static object so that it is able to last for the entire length of the program. Plaground example.

Or, similar to before, you could convert a mutable to a raw pointer to store in the static variable. Now you would need some way to guarantee it is accessed only while the referenced value is still alive.

If you want to use a regular &mut reference, with its built-in performance and safety gurantees, you will need to avoid statics and find a different way to pass it into your code.

1 Like