Skip to content

RFC: allow delegating some methods from an trait impl to a field of a struct #7773

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

Closed
huonw opened this issue Jul 13, 2013 · 7 comments
Closed
Labels
A-attributes Area: Attributes (`#[…]`, `#![…]`) A-trait-system Area: Trait system

Comments

@huonw
Copy link
Member

huonw commented Jul 13, 2013

This would allow one to emulate conventional OO inheritance (to some degree) automatically, instead of requiring a lot of boilerplate, e.g.

trait A {
  fn foo(&self);
  fn bar(&self);
  fn baz(&self);
}

struct Basic {
   some_field: int
}

impl A for Basic {
  fn foo(&self) {}
  fn bar(&self) {}
  fn baz(&self) {}
}

struct Extended {
   inner: Basic,
   extra: int
}

#[delegate_to(inner)]
impl A for Extended {
  fn foo(&self) {} // new version of `foo`
}
/* automatically created:
  fn bar(&self) { self.inner.bar() }
  fn baz(&self) { self.inner.baz() }
*/

This isn't possible as a syntax extension, since the methods in a trait are not known at expansion time. And methods returning Self would have to be implemented by hand.

I guess this is similar to default methods. I think it would allow traits to replace the closures-in-structs pattern entirely (e.g. the ast visitor), since currently it's not easily possible to write Visitor { visit_expr: |e, (a,v)| { ... }, .. some_non_default_visitor } (i.e. replacing only the visit_expr method of a visitor defined elsewhere, which is not the default_visitor()) in terms of default methods only (this came up in my attempt to replace the struct visitors in rustc::middle::lint with @Aatch's trait+default-methods based one).

Related:

A super-wishlist behaviour would be turning any recursive method calls (i.e. calling a method from the same trait) on the base type into calls on the extended type, so:

impl A for Base {
   fn foo(&self) {
       if some_condition { self.bar() }
   }
   fn bar(&self) {}
}

struct Extended { base: Base }

#[delegate_to(base)]
impl A for Extended {}

// is equivalent to

impl A for Extended {
   fn foo(&self) {
       if some_condition { self.bar() }  
       // different to plain self.inner.bar(), which is `if some_condition { self.base.bar() }`
   }
   fn bar(&self) { self.base.bar() }
}

(This is possibly possible by recording "a trait-self" against which to call methods from the same trait, I don't know.)

@MaikKlein
Copy link
Contributor

It would be pretty neat to have something like this in Rust. It would also make it easier to write a wrapper for c++ code.

@dobkeratops
Copy link

+1 i'm interested in this - only single inheritance , its simple from a low level perspective.
i would like the ability to do single inheritance on struct layouts... it would save on navigation/nesting, make code more compact. struct Baz { ...} struct Foo : Baz {..} /* Foo gets all the record slots of Baz, and same impls? */
+1 to easier C++ wrappers

used to do something like "single inheritance" in asm where you simply use the same struct offsets from object pointers.

it looks to me like the compiler source has cases that would be simplified out by single inheritance, i.e. a lot of AST nodes with some common information.

composition is generally superior,but that doesn't mean inheritance is useless.

Is this simple or would implementing this create complex interactions with traits / trait objects? is single inheritance against the design principles of rust?
#1776 -- seems like its been discussed and rejected here

@huonw
Copy link
Member Author

huonw commented Sep 7, 2013

@dobkeratops note that this issue isn't about struct inheritance (i.e. "copying" fields of one struct into another) so much as reducing the boilerplate relating to having a field implementing a certain trait and wanting to reuse this implementation for the overall object. (i.e. one still has the "base" structs as explicit fields.)

@dobkeratops
Copy link

ok . I guess i'm accidently hijacking here.. related but not identical..
single inheritance would handle some subset of whats being proposed here..
imagine replacing the "struct Extended { inner:Basic,...}" with "struct Extended : Basic { ...}" for similar overall result, with the impls' being carried across.

@alexcrichton
Copy link
Member

This may have already been brought up already, but one thing I found interesting about Plan 9's flavor of C (which I believe was adopted in Go as well) was embedding structs in other structs:

struct A { a: int }
impl A { fn foo(&self) {} }

struct B { b: int, A }
impl B { fn bar(&self) {} }

fn main() {
  let b = B { b: 3, A: { a: 3 } };
  b.a; // silently goes through `A`
  b.foo(); // again, goes through `A`
  b.A.a; // explicitly go through `A`
  b.A.foo();
  b.b;
  b.bar();
}

I'm not entirely sure that this would work in rust because shadowing causes real issues, but I always thought it was kinda cool.

@flaper87
Copy link
Contributor

Visiting for triage. Nothing to add here other than this sounds like a good idea and something that could be implemented post 1.0

@rust-highfive
Copy link
Contributor

This issue has been moved to the RFCs repo: rust-lang/rfcs#292

flip1995 pushed a commit to flip1995/rust that referenced this issue Oct 7, 2021
Move module declarations back into lib.rs

With rust-lang#7673 we moved a lot of things from lib.rs to lib.foo.rs. Unfortunately, rustfmt doesn't seem to work when module declarations are included via `include!` (and trying the `mod foo; use foo::*;` trick doesn't seem to work much either in our specific case).

With this PR we continue generating everything in subfiles except for module declarations, which are now generated within lib.rs.

changelog: none
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-attributes Area: Attributes (`#[…]`, `#![…]`) A-trait-system Area: Trait system
Projects
None yet
Development

No branches or pull requests

6 participants