Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is the `Idx` type parameter of the `Index` trait allowed to be unsized?

In Rust 1.14, the Index trait is defined as follows:

pub trait Index<Idx> where Idx: ?Sized {
    type Output: ?Sized;
    fn index(&self, index: Idx) -> &Self::Output;
}

The implicit Sized bound of the Output type is relaxed with ?Sized here. Which makes sense, because the index() method returns a reference to Output. Thus, unsized types can be used, which is useful; example:

impl<T> Index<Range<usize>> for Vec<T> {
    type Output = [T];  // unsized!
    fn index(&self, index: Range<usize>) -> &[T] { … } // no problem: &[T] is sized!
}

The Idx type parameter's implicit bound is also relaxed and can be unsized. But Idx is used by value as method argument and using unsized types as arguments is not possible AFAIK. Why is Idx allowed to be unsized?

like image 926
Lukas Kalbertodt Avatar asked Jan 06 '17 20:01

Lukas Kalbertodt


1 Answers

I'm pretty sure this is just an accident of history. That looser bound was introduced in 2014. At that time, the trait looked a bit different:

// Syntax predates Rust 1.0!
pub trait Index<Sized? Index, Sized? Result> for Sized? {
    /// The method for the indexing (`Foo[Bar]`) operation
    fn index<'a>(&'a self, index: &Index) -> &'a Result;
}

Note that at this point in time, the Index type was passed by reference. Later on the renamed Idx type changed to pass by value:

fn index<'a>(&'a self, index: Idx) -> &'a Self::Output;

However, note that both forms coexisted in the different compiler bootstrap stages. That's probably why the optional Sized bound couldn't be immediately removed. It's my guess that it basically was forgotten due to more important changes, and now we are where we are.

It's an interesting thought experiment to decide if restricting the bound (by removing ?Sized) would break anything... maybe someone should submit a PR... ^_^

Thought experiment over! Lukas submitted a PR! There's been discussion that it might break downstream code that creates subtraits of Index like:

use std::ops::Index;
trait SubIndex<I: ?Sized>: Index<I> { }

There's also talk that someday, we might want to pass dynamically-sized types (DSTs) by value, although I don't understand how.

like image 151
Shepmaster Avatar answered Nov 12 '22 21:11

Shepmaster