Why ops::Index<Idx> allows unsized Idx?

Today I’ve noticed that Index is defined as follows:

pub trait Index<Idx: ?Sized> {
    #[stable(feature = "rust1", since = "1.0.0")]
    type Output: ?Sized;

    #[stable(feature = "rust1", since = "1.0.0")]
    fn index(&self, index: Idx) -> &Self::Output;
}

why do we need Idx: ?Sized if we accept index by value anyway? Shouldn’t https://github.com/rust-lang/rust/pull/23601 removed this anti-bound as well?

One can use unsized_locals for this… Here’s just an example to make it compile:

#![feature(unsized_locals)]

use core::ops::Index;
use core::fmt::Debug;

type A<'a> = &'a dyn Index<dyn Debug, Output = u8>;

fn main() {
    let x: A<'_> = panic!();
    let y: dyn Debug = *(&0 as &dyn Debug);
    x.index(y);
}

If that is useful remains to be seen. However, it is a breaking change to enforce Idx: Sized.

I wonder if there's real code that breaks. rust-lang/rust test suite passes if I remove the bound.

EDIT: which probably means that we either need to remove the bound, or to add a test for it.

Unlikely I think because of the lack of stable unsized_locals. However, I rather leave the un-bound in for future-compat purposes. Is there a specific reason you want to remove ?Sized?

I would happily r+ a PR that adds a test! Thank you for discovering the lack thereof.

1 Like

Consistency: we generally don't have ?Sized bounds unless there's a strong reason to have one. For example, Add is defined as trait Add<RHS=Self>.

I definitely think that we should not blindly add a test for this. I guess I'll send a PR for the libs team to review. It may be the case that we want this to be ?Sized, but than we need to figure out if we can change over operators to follow the suite.

EDIT: PR is up https://github.com/rust-lang/rust/pull/59527

Hmm... I mostly was looking for a use-case. :wink: Consistency is valuable, but I wouldn't want to reduce future potential expressiveness just for that.

The test can easily be removed whenever, there's no problem in adding it now.

1 Like

That’s an extremely personal opinion, but I prefer to reduce expressiveness of things unless there’s a strong reason that expressiveness is required :slight_smile: Expressiveness and “easy to understand” tend to oppose each other (https://www.tedinski.com/2018/01/30/the-one-ring-problem-abstraction-and-power.html is a good blog post on this topic).

In this particular cases, I imagine that, with by-value DSTs implemented, someone might accidentally write Index<[usize]> instead of Index<&[usize]> for multidimensional array indexes, and get an excessive stack usage.

1 Like

I think this is a case of "I cannot tell the future". :stuck_out_tongue:

Seems lintable? We can test this out and see if it actually becomes a problem.

I’d also make Add a subtrait of Sized then. Self and RHS are almost symmetric but only RHS has the Sized bound.

1 Like

DSTs are always passed by pointer, I don't see how fn index(&self, a: [usize]) will cause excessive stack usage.

The current implementation of unsized_locals is naïve and may allocate unnecessary variable-length temporaries to move the slice into.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.