#56805 stabilized Rc and Arc as method receivers, making it possible to do something like:
struct Foo;
impl Foo {
fn foo(self: Arc<Self>) {}
}
However, that forces every call to the method to either have a disposable Arc that won't be used afterwards already, or to Arc::clone, meaning a forced increment and later decrement of the ref counter. (Same with Rc). That can be quite a waste. It gets even worse when the method needs to call other methods that also take self: Arc<Self>.
Stupidly reduced example to illustrate, but the following is not allowed:
struct Foo;
impl Foo {
fn foo(self: Arc<Self>) {}
fn bar(self: Arc<Self>) { self.foo(); self.foo(); }
}
And the only correct way, currently, would be:
struct Foo;
impl Foo {
fn foo(self: Arc<Self>) {}
fn bar(self: Arc<Self>) { Arc::clone(&self).foo(); self.foo(); }
}
The generated code for fn main() { Arc::new(Foo).bar(); } looks like:
playground::main:
pushq %rbx
subq $16, %rsp
movl $16, %edi
movl $8, %esi
callq *__rust_alloc@GOTPCREL(%rip)
testq %rax, %rax
je .LBB5_7
movq %rax, %rbx
movaps .LCPI5_0(%rip), %xmm0
movups %xmm0, (%rax)
lock addq $1, (%rax)
jle .LBB5_8
movq %rbx, 8(%rsp)
lock subq $1, (%rbx)
je .LBB5_3
movq %rbx, 8(%rsp)
lock subq $1, (%rbx)
je .LBB5_5
(snip)
That is extra useless traffic on the atomic refcounter, and real use-cases would actually have to call Arc::clone too, adding one more lock addq. (BTW, it's worth noting the compiler is not assuming the refcounter is not supposed to be decremented more times than it was incremented, and tests whether the refcount reaches 0 on each decrement, although it's not possible for it to reach 0 on the first decrement, since it was increased first (apart from other unsafe code doing nasty stuff to the refcount)) (BTW2: I'm actually surprised the compiler is not able to figure out this reduced testcase is just doing nothing).
A way to address this would be to allow &Arc as a method receiver.
So, would it make sense to stabilize &Arc and &Rc as method receivers, in advance of the full arbitrary self types support? I guess this should also be extended to &Box and &Pin, actually, for similar reasons. It's actually worse for e.g. Box because it means calling a method with self: Box<Self> will always drop the Self.