Arc<(dyn MyTrait, AtomicUsize)> really impossible?

Just to be sure that i have not missed some new feature or syntax, is it possible to create Arc<(dyn MyTrait, AtomicUsize)> or not?

For the context.
The thing is that i am creating a wrapper around Arc and it is necessary to store extra usize information that should be accessed in a shared way. Yes, i can add AtomicPtr in a wrapper itself like Wrapper(Arc<T>, AtomicPtr<AtomicUsize>), but this adds extra indirection and since the wrapper is used in a hot-path, i would like to avoid that.

Conceptually ArcInner is:

struct ArcInner<T: ?Sized> {
    // .. ref-counting stuff..
    data: T,
}

So when using dyn MyTrait theoretically compiler could store passed in tuple (dyn MyTrait, AtomicUsize) at memory location of data: T, as a result one pointer indirection could be avoided, thus faster execution. T in this case could be a tuple of wide pointer + AtomicUsize. Is it not even theoretically possible? Or is it just that this is not implemented? I even got thinking, why not allow Arc<(dyn MyTrait1, dyn MyTrait2)> and/or wider tuples, while not necessary for me at this time, there could be some use-cases where it minimizes indirection.

Some conceptual code to play around.

fn main() {
    let a = MyStruct;
    a.struct_method();
    a.trait_method();
    
    let b = Arc::new(MyStruct);
    b.struct_method();
    b.trait_method();
    
    let c: Arc<dyn MyTrait> = Arc::new(MyStruct);
    c.trait_method();

    // This is somewhat what would like to achieve or to somehow build 
    // a Wrapper(Arc<(dyn MyTrait, AtomicUsize)>) somehow.
    let d: Arc<(dyn MyTrait, AtomicUsize)> = Arc::new((MyStruct, AtomicUsize(0)));
    d.trait_method(); 

}

struct MyStruct;
impl MyStruct {
    fn struct_method(&self) {
        println!("struct method called");
    }
}

trait MyTrait {
    fn trait_method(&self);
}

impl MyTrait for MyStruct {
    fn trait_method(&self) {
        println!("trait method called");
    }
}

use std::sync::Arc;

dyn MyTrait does not have the size of a fat pointer. It's size is not known at compile time. It is a so called dynamically sized type (DST). &dyn MyTrait is a fat pointer.

(AtomicUsize, dyn MyTrait) is also a DST but it has disadvantage that tuples do not implement Unsize i.e. we cannot coerce (AtomicUsize, MyStruct) to (AtomicUsize, dyn MyTrait).

However, you can do what Arc does internally like this:

struct Combined<T: ?Sized> {
    atomic: AtomicUsize,
    my_payload: T,
}

let d: Arc<Combined<dyn MyTrait>> = Arc::new(Combined {
    atomic: AtomicUsize::new(0),
    my_payload: MyStruct,
});
10 Likes

Great. Rust even allows to:

    let d: Arc<Combined<MyStruct>> = Arc::new(Combined {
        atomic: AtomicUsize::new(0),
        data: MyStruct,
    });
    let d: Arc<Combined<dyn MyTrait>> = d;