What is Allocator::by_ref for?

I was looking at the Allocator API that is currently being stabilized and noticed that it has an Allocator::by_ref method.

I don't really understand what that method is for. It seems to simply return a shared reference to the allocator, so how is it any different from a simple borrow? Also the documentation for by_ref talks about returning an adapter of some kind, which it clearly does not do.

I would appreciate any insight or further reading on why this method exists and what the documentation is talking about.

Kinda like https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.by_ref?

That doesn't really explain much. The iterator by_ref has all the same oddities. It seems to do nothing other than return a simple borrow and its documentation also talks about returning an adapter of some kind, which it does not seem to do.

Are these just convenience methods with oddly worded documentation?

Is there some common case of methods taking allocators as Self (as there is with iterators)?

yes, these are convenience methods. the purpose i would think would be made clear by the example in the iterator case. x.by_ref().other_method().yet_another() is meant to be a nicer way to write (&mut x).other_method().yet_another()(yes the parentheses are necesary) or for the Allocator case (&x).other_method().yet_another()

it is quite unclear to me why there is such a method on Allocator though

1 Like

The documentation should maybe actually say this, because I could not for the life of me figure out what it was doing from the existing documentation, but this makes perfect sense.

1 Like

That’s not a good reason to have it for Allocator, though. I can see that someone might want to pass &MyAllocator when constructing a container, but that will never be a method chain.

According to the revision history, by_ref was added in Add a "by reference" adaptor for `AllocRef` by TimDiekmann · Pull Request #71442 · rust-lang/rust · GitHub, which was primarily about adding the &A (at the time, &mut A) implementation. The issue mentions adding by_ref by analogy with io::Write::by_ref(). Which seems to me to be a method with equally little reason to exist, but perhaps I am missing something.

Iterator::by_ref is useful because many of the methods take self, especially when creating new adaptors, but there is also the blanket impl on &mut I so you can have those capture a reference instead via iter.by_ref().foo() -- or equally (&mut iter).foo(). io::Read::by_ref is similar since there are self methods for adaptors.

io::Write and Allocator also have similar blanket impls, but they don't have any self-consuming methods AFAICS, so I don't know why we should want by_ref(). I only found two existing uses of Allocator::by_ref in Arc/Rc::from_box_in, and both would be more succinct to use &alloc.

7 Likes

What's the benefit of these kind of associated functions over impl AsMut<Self> for ...?

And, having AsRef and AsMut in mind, why is it called by_ref and not by_mut although it returns a mutable reference?

Avoiding amiguity for the sake of inference.

Perhaps (in the case of Iterator) it's as simple as, it only makes sense for &mut. (OTOH it's a 1.0 method, there may not have been much conversation. I didn't dig.)

1 Like

these are trait methods. (for example on Iterator), so as_ref is available on every iterator and only on iterators. you cannot impl<I : Iterator> AsMut<Self> for I, that would be against the orphan rule. AsMut ofc is not reflexive.

BorrowMut is relexive, but then it is on every type and can get noisy, as well as conflict with inherent methods.

1 Like