Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to impose a type constraint on the associated type of an associated type (e.g. Iterator::Item)?

I'm trying to define a trait with an associated type. I also want the associated type to implement Iterator with its Item associated type implementing AsRef<str>.

While I know how to do it for a function or for a concrete Iterator::Item type, I can't come up with a clear and concise solution for the original case.

Thanks to the helpful error messages, my compiling solution is:

trait Note
where
    <<Self as Note>::FieldsIter as Iterator>::Item: AsRef<str>,
{
    type FieldsIter: Iterator;
    //other fields and methods omitted
}

The ugly where clause makes me think that there should be a better way.

This doesn't compile since Item: AsRef<str> is an illegal construction:

trait Note {
    type FieldsIter: Iterator<Item: AsRef<str>>;
    //other fields and methods omitted
}

This fails since impl is not allowed here:

trait Note {
    type FieldsIter: Iterator<Item = impl AsRef<str>>;
    //other fields and methods omitted
}

This doesn't compile since I want Iterator::Item to implement a certain trait, not to be a concrete type.

trait Note {
    type FieldsIter: Iterator<Item = AsRef<str>>;
    //other fields and methods omitted
}
like image 805
flopacero Avatar asked Jan 01 '19 19:01

flopacero


People also ask

What type can be specified as a type constraint?

Any value type except Nullable<T> can be specified. You can also specify a generic parameter as a constraint. The type argument supplied for the type you're constraining must be or derive from the type of the constraint. This parameter is called a naked type constraint.

What are constraints in C++?

Constraints inform the compiler about the capabilities a type argument must have. Without any constraints, the type argument could be any type. The compiler can only assume the members of System.Object, which is the ultimate base class for any .NET type.

What is the use of a generic type parameter as a constraint?

The use of a generic type parameter as a constraint is useful when a member function with its own type parameter has to constrain that parameter to the type parameter of the containing type, as shown in the following example: public class List<T> { public void Add<U> (List<U> items) where U : T {/*...*/}

Which constraint should be specified last in a type argument?

The type argument must have a public parameterless constructor. When used together with other constraints, the new () constraint must be specified last. The new () constraint can't be combined with the struct and unmanaged constraints.


1 Answers

You can make one small improvement, but otherwise the current syntax for this is as you have discovered:

trait Note
where
    <Self::FieldsIter as Iterator>::Item: AsRef<str>,
{
    type FieldsIter: Iterator;
}

This is the disambiguated syntax, the only problem is there isn't yet a way to make the ambiguous version! Rust issue #38078 is open to allow the Foo::Bar::Baz syntax.

RFC 2289 is also open as a way to improve this. With the RFC implemented, your second example should work:

trait Note {
    type FieldsIter: Iterator<Item: AsRef<str>>;
}

One way you can work around this now is similar to IntoIterator. This introduces another associated type:

trait Note {
    type FieldsIter: Iterator<Item = Self::Item>;
    type Item: AsRef<str>;
}

I'm not a fan of this because it introduces types that at first look to be orthogonal to each other, but in the end are tightly related.

like image 82
Shepmaster Avatar answered Sep 18 '22 16:09

Shepmaster