Hello, I need a static variable with a dummy value of !Sync type, but the type is !Sync because it has a vector of !Sync elements, so if the vector is empty then the value of a type is Sync. Is there some way / crate to express it without me writing unsafe?
pub struct Foo(pub Vec<Cell<u8>>);
// No way to get `&mut Foo` thus we can't append elements thus no `Cell` thus `Sync`.
static FOO: Foo = Foo(Vec::new());
fn bar(foo: &Foo) {
for element in foo.0.iter() {
element.update(|x| x.wrapping_add(1));
}
}
bar(&'static FOO); // It is fine to use that dummy across the threads
I tried this and understood that my use case cannot be reduced to simply Vec<Cell<u8>>. I have AtomicU8 alongside this Vec - with static it would still be sound as that atomic is Sync and it has exact place.
I was referring to that compiler error, that is also related to this:
Error
error[E0492]: interior mutable shared borrows of temporaries that have their lifetime extended until the end of the program are not allowed
The compiler sees Vec<Cell<u8>> and concludes Foo: !Sync. It can't verify that Vec is empty.
There's no way to express "this type is Sync when the Vec is empty", because that's a runtime invariant, and Rust's trait system only reasons about types.
You hit this either way:
- Cell<u8> field directly -> !Sync -> can't be in static
- Vec<Cell<u8>> field (even empty) -> !Sync -> can't be in static
unsafe impl Sync on a wrapper type is probably the only workaround.
That depiction of "two ways" gave me an idea and it compiles. Basically to create a "ref" type which will have references to atomic and to empty slice of cells. Yeah I would need to use that ref instead of reference which will increase the size, but it is fine.
Not exatcly. The Foo is the same but I added FooRef<'a> that has all fields of Foo but with references - now code internally uses only FooRef and Foo is basically an allocation that user passes. That doesn't require unsafe.