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

Inherent `impl`s for traits #1971

Closed
burdges opened this Issue Apr 13, 2017 · 9 comments

Comments

Projects
None yet
4 participants
@burdges
Copy link

burdges commented Apr 13, 2017

As written, this code works but the commented out line gives error[E0576]: cannot find method or associated constant meow in trait Foo.

pub trait Foo { }

struct Bar;

impl Foo  {
    pub fn meow() {  println!("Hello World!")  }
}

impl Foo for Bar { }

fn main() {
    Foo::meow();
    // <Bar as Foo>::meow();
}

If we want an inherent-like method meow for every type that satisfying Foo, but do not want clients modifying it, then we need a helper trait.

pub trait ImplFoo { 
    fn meow() {  println!("Bye World!")  }
}

impl<T> ImplFoo for T where T: Foo { }

fn main() {
    Bar::meow();
}

Worse, we need new help traits for every inherent-like block we want to create, so our API gets polluted with several exposed helper traits. Also this Foo::meow() looks kinda iffy.

I'd think inherent impls could simply "work as expected" for traits, so all three call sites would call the original :

impl Foo  {
    pub fn meow() {  println!("Hello World!")  }
}

For visibility purposes, these inherent trait methods could be treated as methods on an invisible wrapper struct that has the same name as the trait Foo:

struct Foo<T: Foo>(T);
impl<T: Foo> Foo<T> {
    pub fn meow() {  println!("Hello World!")  }
}

In this way, if Foo is defined in module A and meow in module B then the visibility of meow can be controlled as you would control the visibility of B::Foo.

@mark-i-m

This comment has been minimized.

Copy link
Contributor

mark-i-m commented Apr 14, 2017

I didn't even know you could impl Trait {...}! I am curious now about the use case for this. Can you give an example of when you might want to use this in real code?

Also, would the same purpose be achieved if we had something like

pub trait Foo {
    #[no_override] // specifies that implementors cannot override the default
    pub fn meow() {  println!("Hello World!")  }
}
@eddyb

This comment has been minimized.

Copy link
Member

eddyb commented Apr 14, 2017

@mark-i-m No, this is not about methods of the trait, but methods for the trait object.
For example, impl Any { fn downcast_ref<T>(&self) -> Option<&T>; } will end up with self: &Any.
Not an arbitrary implementer of the trait, but only the trait object itself gets downcast_ref.

@mark-i-m

This comment has been minimized.

Copy link
Contributor

mark-i-m commented Apr 14, 2017

@eddyb Thanks for the clarification! That makes a lot more sense now 😄

@burdges

This comment has been minimized.

Copy link
Author

burdges commented Apr 15, 2017

I see, thanks for the explanation @eddyb. In that case, the syntax I'm proposing should maybe be impl<T> Self<Foo> { } or impl<T> Inherent<Foo> { } or impl<T> impl Foo { } or such.

In some cases, one can write (&x).meow() as a work around, maybe one can even avoid the & if deref can return a trait object, except object safety becomes an inconvenience.

In that vein, Deref could make a nicer helper :

struct ImplFoo<T: Foo>(T) where T: Sized;

impl<T: Foo> Deref for T {
    type Target = ImplFoo;
    pub deref(&self) -> &ImplFoo { unsafe { transmute(self) } }
}
impl<T: Foo> DerefMut for T {
    pub deref_mut(&mut self) -> &mut ImplFoo { unsafe { transmute(self) } }
} 

impl<T: Foo> ImplFoo<T> {
    pub fn meow() {  println!("Hello World!")  }
}

At least this way the inherent impl is a real inherent impl so you can create multiple impl blocks without needing numerous healer traits. It would not for Bar::meow() though.

Also, I can improve the original trait based work around with

pub trait Foo : ImplFoo { }
impl<T> ImplFoo for T where T: Foo { .. }
pub trait ImplFoo { .. }
@burdges

This comment has been minimized.

Copy link
Author

burdges commented Apr 17, 2017

I suppose the syntax impl<T: Foo> T works best, assuming this all makes sense. Alternatively, one could imagine some "transparent" wrapper struct scheme, which might let you provide local impls for traits too, but I'd think delegation sounds like a more likely route to that.

@mark-i-m

This comment has been minimized.

Copy link
Contributor

mark-i-m commented Apr 17, 2017

I feel like impl<T: Foo> T would get confused easily with impl<T> Foo for T

@eddyb

This comment has been minimized.

Copy link
Member

eddyb commented Apr 17, 2017

Frankly it's correct syntax if you want to add methods to all the types that implement a trait.
This comes for free with crate-private inherent impls for out-of-crate types, so you should focus on that.

@burdges

This comment has been minimized.

Copy link
Author

burdges commented Apr 17, 2017

I'd think impl<T> Foo for T should normally fail, while the inherent impl<T> T where T: Foo always succeeds. Oh I see. You're worried folks would accidentally write the inherent, when they really mean the generic trait version.

A transparent wrapper scheme might look like an attribute like say #[reverse_delegate] or #[auto_wrap] on a wrapper struct restricted to the trait.

@Centril

This comment has been minimized.

Copy link
Contributor

Centril commented Oct 8, 2018

Closing in favor of #1880.

@Centril Centril closed this Oct 8, 2018

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.