I believe this falls victim to the same problem that GATifying the Index
trait falls into: the arr[ix]
syntax is not sugar for a function call. It's much more complicated than that.
The result of an expression arr[ix]
is, in Rust terminology, a place. (In C++ terms, an lvalue.) In Rust, this isn't a "thing" that you can return from a function or bind in a binding, so it's kind of difficult to talk about. (In C++, you can have an lvalue reference, T&
, which makes this kind of discussion easier. In fact, in C++, indexing is just sugar for a function returning an lvalue reference (with overloading semantics).) A place is effectively "what you get from a dereference operator".
What makes a place special is that while it's typed at T
, you can use it without moving the value. When you take a reference to a place, you get the same reference that was dereferenced to obtain the place.
So, okay, indexing is sugar for not arr.index(ix)
, but *arr.index(ix)
, so that you can take it's address with &
or &mut
,,, and it... somehow decides whether to call index_mut
instead, if you "use the place via mutable reference".
And this interaction with the context is what really drives the nail in the coffin of returning anything other than references from index, imho. You could maybe get around the immediate dereference by just saying that &arr[ix]
returns your special handle, rather than a regular reference, and the same for &mut arr[ix]
.
But how do you resolve method syntax?
Via autoref, .method()
can either use the place by-move, by-ref, or by-mut. This is resolved by the signature of T::method
and what the receiver type is, and then that receiver type is back-propagated to decide whether to call Index::index
or IndexMut::index_mut
.
There is no way to resolve this for custom handle types. The behavior of indexing syntax is tied to the details of returning built-in references in order to be a place expression and act how people expect.
I'm pretty sure I said it the last time I talked about this, but I really should submit a design notes document for "why Index
can't be GATified".
And now I have to note that you've somewhat sidestepped this issue with the OP. Notably, you've:
- Used a theoretical mutually exclusive trait to the
Index
trait, avoiding Index
compatibility hazards;
- Mandated
&mut
access to the indexee, avoiding resolution concerns;
- Mandated returning a (boxed dyn future returning an) owned value, avoiding the problem of returning a place; and
- Hidden complexity behind
#[async_trait]
(though this (async fn in traits) is a likely future feature).
So maybe my whole rant about GAT Index
doesn't apply. But I think the spirit of the issue remains: Index
is special, and your IndexAsync
isn't special, it's just a regular function call.
So really, the question stands: why should this be put on the []
syntax, when it behaves quite unlike the existing []
syntax? If your goal isn't to produce a place, what you want is a function call.