In general, I like this. Although trying to use this feature as a way to simulate Go's struct embedding seems a bit misguided. Besides the fact that this would allow you to say *bar to get the embedded struct, which seems unintentional, it also won't work if you want to embed two separate structs and allow for delegating methods to both of them.
I don't have a solution off-hand for simulating embedding of two separate structs w/method lookup though. -Kevin On Mon, Jul 29, 2013 at 10:28 PM, Patrick Walton <[email protected]> wrote: > Hi everyone, > > I've recently started thinking that a number of use cases that we've wanted > to solve at some point could be solved if the dereference operator could be > overloaded much like the other operators. Most importantly, this addresses a > missing part of the puzzle for custom smart pointers, but it also fixes > issues relating to autoderef on newtypes and "common fields". > > # Mechanics > > We introduce a new lang item trait: > > #[lang="deref"] > pub trait Deref<Result> { > fn deref(&'self self) -> &'self Result; > } > > This `deref` method is invoked by the compiler in two cases: > > 1. When the unary `*` operator is used on a value. In this case, the result > pointer type is automatically dereferenced and becomes an lvalue (albeit an > immutable one). > > 2. When method lookup or field projection fails. In this case, the method > lookup or field projection is tried again with the `Result` type. > > It would be nice if `Result` were a functional dependency of `Self` above > (e.g. simultaneous `impl Result<int> for Foo` and `impl Result<float> for > Foo` would be forbidden). Unfortunately we don't have the trait machinery to > enforce this yet, as this is associated types. We could just enforce this in > an ad hoc way, or we could not enforce it. I don't care too much either way. > > # Use cases > > There are several use cases that this enables: > > ## Custom smart pointers > > For custom smart pointers it is highly desirable to support autoderef and to > have the `*` operator enable access to fields. For example, suppose `@T` > becomes `Gc<T>`. We would like to avoid something like: > > let p: Gc<int> = ...; > do p.read |x| { > printfln!("Your lucky number is %d", *x) > } > > With overloadable deref it would look like: > > let p: Gc<int> = ...; > printfln!("Your lucky number is %d", *p) > > I *believe* that this does not cause liveness issues for GC and RC because > the lifetime of the resulting reference is tied to the lifetime of the GC/RC > box itself, so the reference piggybacks off the pointer's reference count > and everything is OK. However, I could be mistaken here; here I'd like > others to check my reasoning. In particular I'm also interested in > legitimate use cases that this might forbid. > > ## Controllable newtype autoderef > > Currently, newtype structs automatically dereference to the value they > contain; for example: > > struct MyInt(int); > fn main() { > let x = MyInt(3); > printfln("1 + 2 = " + x.to_str()); // prints "1 + 2 = 3" > } > > This behavior is sometimes undesirable, as Brian often points out. Haskell > allows behavior similar to this to be controlled on an opt-in basis with > `GeneralizedNewtypeDeriving`. We could support something similar by turning > off autoderef for newtype structs and leaning on overloadable dereferencing > when it is desirable. In this new world, to get the behavior above one would > write: > > struct MyInt(int); > impl Deref<int> for MyInt { > fn deref(&'self self) -> &'self int { > let MyInt(ref inner) = *self; > inner > } > } > > We could imagine something like this to make it simpler: > > #[deriving(Deref)] > struct MyInt(int); > > ## Anonymous fields > > In Go (and in C with Plan 9 extensions) it is possible to place one struct > inside another struct and inherit its fields: > > type Foo struct { > X int > Y int > } > > type Bar struct { > Foo > Z int > } > > x = Bar { > Foo { > X: 1, > Y: 2, > } > Z: 3, > } > fmt.Println("%d", x.Y) // prints 2 > > This is almost multiple inheritance, except that the type of the `this` > pointer will be different when invoking `Foo` methods on a `Bar` instance. > > With overloadable deref this would be possible in Rust as well: > > struct Bar { > base: Foo, > z: int, > } > > impl Deref<Foo> for Bar { > fn deref(&'self self) -> &'self Foo { > &self.base > } > } > > One could imagine macro sugar for this use case, for example: > > #[deriving(Deref(base))] > struct Bar { > base: Foo, > z: int, > } > > ## Common fields > > It is a common pattern, for example in Servo, to simulate inheritance in > Rust with something like: > > struct Bar { > base: FooCommon, > ... > } > > struct Baz { > base: FooCommon, > ... > } > > struct Boo { > base: FooCommon, > ... > } > > enum Foo { > BarClass(~Bar), > BazClass(~Baz), > BooClass(~Boo), > } > > The problem here is that if you have a `Foo` instance there is no convenient > way to access the common fields short of a `match`. Again, overloadable > deref comes to the rescue here. We could imagine an overloaded `Deref` as > follows: > > impl Deref<FooCommon> for Foo { > fn deref(&'self self) -> &'self FooCommon { > match *self { > BarClass(ref bar) => &bar.base, > BazClass(ref baz) => &baz.base, > BooClass(ref boo) => &boo.base, > } > } > } > > And once again we could come up with some sort of syntactic sugar for this. > > # Conclusion > > This one small feature seems to encompass a lot of use cases which we had > previously thought we might have to solve using multiple disparate features. > That cleanliness is attractive to me, assuming that this scheme works. I'd > be interested to hear everyone's thoughts. > _______________________________________________ > Rust-dev mailing list > [email protected] > https://mail.mozilla.org/listinfo/rust-dev _______________________________________________ Rust-dev mailing list [email protected] https://mail.mozilla.org/listinfo/rust-dev
