Global mutable trait object possible?

Hi ... This question is not asking why I shouldn't use a global variable or asking what is a much better way to do this besides using globals, I just want to first understand this exercise. Specifically I have two related questions on the topic:

First, how would I modify the syntax on lines 21 and or 28 here: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=c6923e21b43dcdf636b7900b2d8713ed In order to get this code to compile. What I am attempting to have here is a reference to an object that implements the trait FooInterface. Moreover, I want the global to be a mutable reference and I want the the object being pointed to to also be mutable. Is this possible with minor changes to my syntax at the playground.

The second question is about the lack of an error message about "statics require immutable values". I can not even include &mut DefaultFoo on my system which is running 1.40 nightly but the playground lets me write this syntax with out error. Is this syntax that was allowed before but now isn't?

I hope I've asked a question that makes sense and in a way that makes sense. Thanks.

You need to be careful about distinction between mutable binding and mutable borrow.

  • let mut x = &1 is a mutable binding to an immutable borrow (you can assign to x, but you can't change 1).
  • let x = &mut 1 is an immutable binding to a mutable borrow (you can change 1, but you can't assign to x).
pub static mut GLOBAL_FOO: &dyn FooInterface

is a mutable binding (static mut) to an immutable value (&dyn, not &mut dyn).

@kornel Ok thanks. I see except .. I've actually tried the change with variants a few times before and the compiler would always complain about syntax or semantics. Here is the change at playground:

pub static mut GLOBAL_FOO: &dyn FooInterface = &DefaultFoo {};

fn main() {
    unsafe{
        GLOBAL_FOO = &mut SomeNonDefaultFoo{};
        GLOBAL_FOO = &mut SomeOtherNonDefaultFoo{};
        let my_foo = FooInterface::new(GLOBAL_FOO);
    }
}

leads to:

error[E0308]: mismatched types
  --> src/main.rs:27:40
   |
27 |         let my_foo = FooInterface::new(GLOBAL_FOO);
   |                                        ^^^^^^^^^^ types differ in mutability
   |
   = note: expected mutable reference `&mut _`
                      found reference `&'static (dyn FooInterface + 'static)`

Which is not the syntax I want any way as you clearly explain. But when I did try to modify the syntax to have a mutable borrow my changes here:

pub static mut GLOBAL_FOO: &mut dyn FooInterface = &mut DefaultFoo {};

lead to the following error:

error[E0658]: references in statics may only refer to immutable values
  --> src/main.rs:21:52
   |
21 | pub static mut GLOBAL_FOO: &mut dyn FooInterface = &mut DefaultFoo {};
   |                                                    ^^^^^^^^^^^^^^^^^^ statics require immutable values
   |
   = note: for more information, see https://github.com/rust-lang/rust/issues/57349

statics require immutable values. This error is why I was wondering if what I am trying to do is even possible?

Yes, "references in statics may only refer to immutable values" is your answer :slight_smile:

In practice you can use Mutex<Box<dyn FooInterface>> or some other variant of interior mutability.

@kornel .. Thanks.

Would like to carry this a little further.

The answer seemed to be that pub static is not something rust does for a mutable data .. at least not the way I was trying to write it for an interface type. @kornel suggested

Mutex<Box<dyn FooInterface>>

which I think makes sense to me as the Mutex should mean only one thread is accessing the pointer at a time and so safety is provided at that level for global data. However, I can create this globally. I have a modified playground here for the original example:

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=5b9d2582d90bcbd7b1154c8f881cf9c0

Right now I am not even concerned about the mut aspect of the data pointed to by the box.

static mut DEFAULT_FOO: DefaultFoo = DefaultFoo {};

lazy_static!{
static ref GLOBAL_FOO: Mutex<Box<&'static dyn FooInterface>> = Mutex::new(Box::new(*DEFAULT_FOO));
}

This code has evolved as I've tried many different ways to write this but the latest errors have rust complaining that I can't figure out how to describe to rust a lifetime for the FooInterface type. But when I give it one it either complains about the lifetime not being defined or about not being able to safely share between threads. I thought I'd read somewhere that dyn trait doesn't implement sync but since I am behind a Mutex I thought this code would be Ok. The code above produces the following error:

  Compiling playground v0.0.1 (/playground)
error[E0277]: `(dyn FooInterface + 'static)` cannot be shared between threads safely
  --> src/main.rs:34:1
   |
34 | / lazy_static!{
35 | |     static ref GLOBAL_FOO: Mutex<Box<&'static dyn FooInterface>> = Mutex::new(Box::new(*DEFAULT_FOO));
36 | | }
   | |_^ `(dyn FooInterface + 'static)` cannot be shared between threads safely

How do I declare a global Mutex<Box>? Really I am back to my original question of if a global mutable trait object is possible in Rust? Note, I've seen a couple of related threads here: Specifying lifetimes in a static ref and Struggling with static lifetimes and borrowed values but so far I am still trying to just get a handle on what's possible here.

The errors about thread safety here are because the dyn trait object needs to implement Sync, an extra trait bound fixes that error:

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=0045cb4c70dfd2007050db023efc0cd1

2 Likes

That was straightforward. You can get lost in understanding what the borrow checker wants you to do. Thank you!

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.