#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
.