Feature Request: add a trait the same as std::ops::Index but returns an object rather than a reference

Description: Add a trait the same as std::ops::Index except the return of fn index is Self::Output rather than &Self::Output.

Motivation: Please consider this code

pub trait MyIndex<Idx>{
    type Output: ?Sized;
    fn index(&self,index:Idx)->Self::Output; //Notice the return value here
}

struct Foo<'a>(std::collections::HashMap<u32,&'a str>);

impl MyIndex<u32> for Foo<'_>{

    type Output=String;
    fn index(&self,index:u32)->Self::Output{
        let mut s=self.0[&index].to_owned();
        s.make_ascii_uppercase();
        s
    }

}

To make the example simple I just used make_ascii_uppercase as mutable operation, but in real practice, it probably contains much more complicated steps to get the value and process it, that's why I raised this topic.

1 Like

Generally, the indexing syntax is useful for code clarity in that it returns a view (reference) into the indexed object.

What benefit (other than terseness) do you get by returning an owned 'static value from indexing? That isn't provided by just a .get()?

Additionally, what happens if you implement both Index and MyIndex? If you can't, how is that enforced?

Previously:

(Wow I've argued this point a fair number of times... indexing returning a custom type seems like an obvious extension, it just runs into a whole thorny bush of issues once you start digging into the implications. Maybe I should write up some lang design notes to this effect...)

5 Likes

how about:

pub trait Index2<I> {
    type Output;
    fn index(self, index: I) -> Self::Output;
}

impl<'a, T: Index<I>> Index2<I> for &'a T {
    type Output = &'a <T as Index<I>>::Output;
    fn index(self, index: I) -> Self::Output {
        Index::index(self, index)
    }
}

// similar impl for IndexMut

The question then though is how do you choose between Index2 for &T and Index2 for &mut T when you're indexing T? It's decided via autoref rules today, and won't work without autoref extensions for Index2.

Honestly, I'd read both my linked threads (they're relatively short) and unless you (the proposer in general) have answers to my concerns raised in the threads, I doubt any new index trait will work (without more mountains of special behavior), let alone be worth the churn of changing how indexing is done.

2 Likes

I tried proposing the change here, but ultimately shelved it because of a lack of time and realizing custom DSTs are the way for a lot of this.

There was a post somewhere explaining how a lack of reborrowing makes returning structs pretending to be refernecs would be very painful, and also there would probably be a lot of churn. As written, they wouldn't allow returning references to data, ruling out stuff like NumPy views, leaving the main use case being BitSets and IndexMut for HashMap.

The existing behavior of code related to the Index traits is non-trivial, but it's also not overly complicated either. I would surmise the hardest part would be demonstrating that these traits would be worth the increased complexity in the indexing process.