Unless you store Rows in the Image (while making the whole structure non-self-referential) and hand out references to those, I don't see how you can satisfy the fact that Index::index returns &Self::Output not Self::Output.
I thought as much, thank you. I guess it boils down tho this. It's a shame that the Index trait was not designed in a more flexible manner that allows returning owned values.
AsRef and Borrow are other examples of traits that only allow you to hand out references to data already stored in the implementing type, not construct something new in the method body and return that.
Indeed, but this is less surprising, since the fact, that a reference is returned can be inferred from the name. And implementing a trait that is intended to return a reference (or a "borrow") to return an owned value would be nonsensical imo.
Otoh, imho returning a view or proxy object from an indexing operation is a valid use case.
I agree. My guess to why Index, AsRef and Borrow are designed the way they are is to avoid surprises on the user's side. I.e. I'd say the expectation of these operations is that they are cheap and there is no complex, potentially computationally expensive logic going on in them.
It was designed for things like method chaining or modifying in place, which is why
img[0]
notionally desugars to one of
// v dereferences to a place expression
*<Image as Deref>::index(&img, 0)
// v dereferences to a place expression
*<Image as DerefMut>::index_mut(&mut img, 0)
which is incompatible with your trait. The context sensitivity is also why IndexMut<_> uses Index<_>'s associated type, ala Deref/DerefMut, so the type of the place expression is the same in both cases. They would probably also be plagued with inference problems if the output were a generic parameter and not an associated type.
IndexOwned<_> or such would probably require mutually exclusive traits or such to be viable with inference. Or perhaps a strict order of preference, but then implementing traits would be a breaking, semantic-altering change.[1]
GAT/non-reference versions of Borrow, AsRef, and AsMut are more tenable, since they're not "mutable context sensitive" operators nor auto-dereferencing place expressions. They might have been designed differently if Rust 1.0 had GATs.
But Deref/DerefMut/Index/IndexMut, less likely IMO, at least not without more language features we don't yet have today.
OTOH this can already be the case given method resolution âŠī¸
That's a sensible workaround. However, I'd still need to construct the u8 from the inner u32, which is an owned value. Who will own it when I return the reference to it?
I might implement const ZERO: u8 = 0; const ONE: u8 = 1; ... up to u8::MAX and return references to those. But this seems a bit hacky.