Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generic impls rely on syntax too much #1162

Closed
petrochenkov opened this issue Jun 16, 2015 · 6 comments
Closed

Generic impls rely on syntax too much #1162

petrochenkov opened this issue Jun 16, 2015 · 6 comments
Labels
T-lang Relevant to the language team, which will review and decide on the RFC.

Comments

@petrochenkov
Copy link
Contributor

References, raw pointers, arrays etc. have distinct syntactic forms and as a consequence 1) traits can be implemented for them and 2) coherence checker knows things like "references are not arrays"

impl<'a, T> Tr for &'a T {}
impl<T> Tr for *const T {}
impl<T> Tr for [T] {}

On the other side, trait objects, non-references, non-pointers, unique function types, tuples of any size and other sets of types don't have distinct syntactic forms and that's the only (?) reason why they cannot be used with traits.
Assume for a second that non-references have their own syntax - (╯°□°)╯︵ & - then we could likely write

impl<'a, T> Tr for &'a T { /*one implementation*/ }
impl Tr for (╯°□°)╯︵ & { /*another implementation*/ }

and coherence checker would know that "references are not non-references". And it wouldn't require any new complex machinery - negative bounds, specialization - beyond the existing one.

Conclusion: We should invent a way to write impls for built-in (fixed, not defined by traits), but syntactically inexpressible sets of types.

@eefriedman
Copy link
Contributor

#376 proposes allowing something like impl<..T> Tr for (..T) {}. I think that covers most of the relevant use-cases?

@arielb1
Copy link
Contributor

arielb1 commented Jun 17, 2015

@petrochenkov

It is not really based on syntax, but rather on type-constructors. Rust's type system is based on type constructors, and will be in the foreseeable future.

@withoutboats
Copy link
Contributor

By non-reference, I assume you mean an owned value that isn't a pointer of any kind (e.g. not &, &mut, *const, or *mut). But these pointers are owned values, and just types like any other type. What about library defined pointer types, like Rc<T>? With mutually exclusive traits, you would be able to implement a trait for T where T: !Deref, but that wouldn't cover most types under the most recent RFC (#1148) because of the backcompat hazard.

If you mean to implement traits for every type that isn't a particular type, that is a different thing, with other implications. But there's nothing special about a non-reference type any more than non-TcpStream type.

@petrochenkov
Copy link
Contributor Author

@eefriedman
Surely, variadics would be helpful for tuples and function parameters, but they are not a comprehensive solution. You still can't, for example, implement Clone or Debug for all function types (TyBareFn(..)).

@arielb1
What do you mean by type constructors - ty::TypeVariants, or is there something else similar but distinct used in trait resolution? (I'm not familiar with the implementation.)
Variants of ty::TypeVariants are indeed look like type constructors to me - they take some arguments an return types.
But the arguments are not necessarily types or lifetimes (or type-lists/lifetime-lists with variadics) and exactly these cases - non-type and non-lifetime arguments - can't be expressed syntactically in the source code.
Ability to match on variants of ty::TypeVariants in some form is what I would probably like to see as a solution for this issue:

// Implement `Clone` for fixed arrays
// The syntax is for exposition only
impl<T: Clone> Clone for $ty::TypeVariants::TyArray(T, _)$ {
    fn clone(&self) -> Self {
        *self
    }
}

(Negation on type constructors is probably a separate problem, yes.)

@withoutboats
Non-reference means any type which is not &T/&mut T.
One example of why is it needed: rust-lang/rust#23521
(BTW the issue linked from that PR (rust-lang/rust#23519) shows why semantics of default impls (impl Tr for ..) inherited by RFC 1148 is inappropriate for such tasks.)
In the long run, well designed specialization/negative bounds involving traits would be much better solution for tasks like impls for non-references, but they are also much more complex.


If Rust gets specialization and sealed traits, then we could copy the C++'s approach to the problem.
libcore (with help of the compiler) could provide a set of sealed type traits and users could write impls like:

impl<T> fmt::Pointer for T where T: IsFunction {
    fn fmt(&self, f: &mut Formatter) -> Result {
        fmt::Pointer::fmt(self as *const (), f)
    }
}

Note, that Rust unlike C++ performs type checking before monomorphization, so traits like IsFunction have to be known to the compiler, because operator as in self as *const () can't be used on generic self, but can be used on function types and the compiler should know that Self is indeed a function type if it has bound IsFunction.

@arielb1
Copy link
Contributor

arielb1 commented Jun 20, 2015

@petrochenkov

Not really. There's a TyStruct variant, but each struct is its own type constructor. We probably do want a way to match all functions/all arrays of a type, through.

Anyway, this wouldn't work with traits because we don't support sealed/exclusive traits, and adding that would be rather complicated.

@bluss
Copy link
Member

bluss commented Jun 23, 2015

Rust could recognize other classes of types, say if I wanted to impl a trait for all anonymous lambda expression types, or all tuple types, etc.

@nrc nrc added the T-lang Relevant to the language team, which will review and decide on the RFC. label Aug 29, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
T-lang Relevant to the language team, which will review and decide on the RFC.
Projects
None yet
Development

No branches or pull requests

6 participants