Pre-RFC: Returning Proxies from Index/IndexMut

&var[idx] isn't translated to &(Index::index(&var, idx)). It's translated to Index::index(&var, idx). The introduced reference is "lost", and effectively it is your var.index_by_ref(idx), as var.index(idx)/Index::index(&var, idx).

The point I'm trying to convey is that &var[idx] evaluating to a type that isn't &_ is going to be surprising, and lose the affordance that the current syntax has.

On top of this, if [] doesn't always return T, &T, or &mut T, but instead, for example, returns Result<T, E>, Result<&T, E>, or Result<&mut T, E> based on usage, how do we actually define when what type is made available? How does this interact with method resolution?

Autoref works on var[idx] because it's a "place" computation; I think a purely syntactical expansion of indexing (if you ignore IndexMut for a moment) would work as *Index::index(&var, idx). That way, auto-ref or a literal take-ref operator just "re-establish" the reference to the place being indexed. A change to indexing to not just work with plain references, though convenient, would require deep changes to how indexing is lowered (beyond just changing the method called) and auto-ref/typeck/inference around any and all indexing operations.

As a final bit, the syntax for indexing isn't &var[idx] and &mut var[idx]; it's var[idx]. Whether indexing is done via Index::index or Index::index_mut is decided (by typeck?) by whether the indexing place is used by-move, by-shared-reference, or by-unique-reference. But crucially, the place itself is of a known type.

Because of the loose-binding of the reference-of operator &, I don't think anyone would like it if to use indexing the reference-of operator needed to be present rather than autoref magic just doing its thing. Then we'd have to have a lot of (&var[idx]).method() rather than just var[idx].method().

IMHO, wrapper types for indexing is a great idea/concept that falls apart once you try to stick it onto indexing syntax rather than method syntax. (In fact, this discussion has sent me back a good ways in language design for my toy language, as the previous concept of making indexing "just" a function call falls apart in the face of this same issue, just with different syntax (since I have the same shared/unique split).)


Disclaimer: all further discussion of affordances is theoretical and backed up by nothing but annecdotal evidence of my interactions with other developers, and my own intuition.

Sure, the affordance of indexing is basically based on how it works in other C-styled languages. But today, &var[idx] translates roughly to "take a reference of the place found by indexing var by idx". There's no affordance for a move involved; the intuition is that var[idx] is a "place" computation. (In fact, IIUC, this is (basically) true for current Rust.)

3 Likes