[rust-dev] Inheritance / delegation

David Henningsson diwic at ubuntu.com
Fri Oct 17 12:30:07 PDT 2014


First, big thanks to both of you, Ben and Clark, for helping out. :-)

Thanks for the hint on the lifetime syntax - it's a bit hard to grasp, 
sometimes it's "Foo<'a>", sometimes it's "& 'a [Foo]", and I've seen 
examples with "Foo+'a" too. But maybe this all makes sense when I'm more 
used to the language.

As for how long the pointed-to data is alive for, I think it makes sense 
to default to "the same lifetime as parent struct or enum", if the 
lifetime is not explicitly specified.

Btw, for some reason this does not work:

let s = InBag(Bag { price: 10, weights: &[3, 5, 7] }); /* fails with 
"borrowed value does not live long enough" and "consider using a `let` 
binding to increase its lifetime" */

But the below does, and can be used as a workaround:

    let b = Bag { price: 10, weights: &[3, 5, 7] };
    let s = InBag(b);

I'm sure there's an explanation to why, but if it can be fixed so that 
the compiler interprets the first as being equivalent to the second it 
would be nice.

On 2014-10-17 21:01, Ben Foppa wrote:
> Your struct has a fixed size - a reference is a pointer. Which is why 
> it requires a lifetime - how long is the pointed-to data alive for? 
> And so you need to tell it - in your enum example, you need to say 
> Bag<'a> (for some defined 'a). For example, Bag<'static> means the 
> pointed-to data lives as long as the program.
>
> On Fri, Oct 17, 2014 at 11:50 AM, David Henningsson <diwic at ubuntu.com 
> <mailto:diwic at ubuntu.com>> wrote:
>
>     Thanks for the answer. Deref is a new one for me, looks
>     interesting by means of abstraction.
>
>     I'm also coming from the C camp but I'm not sure how to write the
>     code that I want in rust. Yet. :-)
>
>     E g, here's one, somewhat related, C example that I'm not sure how
>     to do in Rust:
>
>     struct bag {
>        int price;
>        int nitems;
>        int []items;
>     };
>
>     struct cart { /* ... */ };
>
>     struct shopping {
>        int kind; /* 0 means bag, 1 means cart */
>        union {
>           struct bag bag;
>           struct cart cart;
>        }
>     }
>
>     struct shopping* make_shopping_bag(int price, int nitems, int *items)
>     {
>         struct shopping* result =
>     malloc(sizeof(shopping)+nitems*sizeof(int));
>         result.kind = 0;
>         result.bag.price = price;
>         result.bag.nitems = nitems;
>         memcpy(result.bag.items, nitems*sizeof(int));
>     }
>
>
>     So, the bag struct would probably look like this in Rust:
>
>     struct Bag <'a> {
>       price: int,
>       weights: & 'a [int],
>     }
>
>      1) It feels like "weights: [int]" would be more like the way I
>     want it, and the declaration compiles, but i can't initialize the
>     struct or have it as a local variable because its size is now
>     unkown. Even though a static initialization like "let b = Bag {
>     price: 10, weights: [3,5,7] }" would be trivial to calculate the
>     total size of, the compiler seems not to be able to do this.
>
>      2) I'm not sure why this lifetime has to be explicit, should
>     default to "same lifetime as parent struct" IMO.
>
>      3) And when I try to do like:
>
>     enum Shopping {
>       InBag(Bag),
>       InCart( /* ... */ ),
>     }
>
>     I get an error: "error: wrong number of lifetime parameters:
>     expected 1, found 0". I've tried both "InBag('a Bag)" and
>     "InBag(Bag + 'a)" but that ends up with other errors instead...so
>     no idea on what to do about that?
>
>
>     On 2014-10-17 18:37, Clark Gaebel wrote:
>>     Rust is not a replacement for java, it’s a replacement for C and C++.
>>
>>     To solve little “puzzles” like this, i tend to ask myself “how
>>     would I do this in C”, and then write that code in rust. Building
>>     inheritance trees is generally the wrong way of approaching
>>     problems. In cases where it does apply, you can still do it, but
>>     be gentle. Try not to lean on them as your primary means of
>>     abstraction.
>>
>>     Anyhow, on to your actual problem.
>>
>>     Something which might be worth trying is implementing
>>     `Deref<Circle>` and `DerefMut<Circle>` for your pancake, then
>>     having a `DList<Box<Deref<Circle>>>` (or just use a normal &, if
>>     you want that).
>>
>>     Then you can call all your circle traits after a quick call to
>>     `.deref()`, AND your `DList` will free everything properly.
>>
>>     But again, there’s probably a simpler solution that doesn’t
>>     involve “inheritance” that you should consider. Maybe a DList of
>>     enums? Maybe just a Vec<uint> in this case? Think about how you’d
>>     do it in C.
>>
>>     Regards,
>>       - Clark
>>
>>
>>
>>     On Fri, Oct 17, 2014 at 4:27 AM, David Henningsson
>>     <diwic at ubuntu.com <mailto:diwic at ubuntu.com>> wrote:
>>
>>         Hmm, right. The as_* could probably useful to write a macro for.
>>
>>         Coming from the C/Java side of things I have to figure out
>>         how this
>>         works in a bigger context, e g a DList or other structure
>>         owning objects
>>         implementing HasArea. This seems to compile, e g:
>>
>>         impl Pancake {
>>         fn as_box_circle(&self) -> Box<Circle> { box self.circle }
>>         }
>>
>>         fn make_pancake(dl: &mut DList<Box<HasArea>>) {
>>         let p = Pancake { circle: Circle { x: 0f64, y: 0f64, radius:
>>         1f64
>>         }, is_tasty: true };
>>         dl.push(p.as_box_circle());
>>         }
>>
>>         But I'd assume that make_pancake would now make a copy of the
>>         pancake's
>>         circle, rather than taking ownership of the entire pancake,
>>         right? The
>>         pancake then gets dropped at function return.
>>
>>         In this simple example perhaps this does not make that much of a
>>         difference though, but if you imagine a C struct like:
>>
>>         struct list {
>>         list *next;
>>         circle *data;
>>         }
>>
>>         You can now put a pointer to a pancake as data, use it as a
>>         circle, and
>>         when you finally free the list and the data that goes with
>>         it, the
>>         entire pancake will be freed. This you cannot do in rust...or
>>         can you?
>>
>>
>>         On 2014-10-17 07:59, Clark Gaebel wrote:
>>         > impl Pancake {
>>         > fn as_circle(&self) -> &Circle { &self.circle }
>>         > fn as_mut_circle(&mut self) -> &mut Circle { &mut
>>         self.circle }
>>         > }
>>         >
>>         > The compiler will optimize trivial functions, except
>>         cross-crate. In
>>         > those cases, use an #[inline] annotation.
>>         >
>>         >
>>         >
>>         > On Thu, Oct 16, 2014 at 10:57 PM, David Henningsson
>>         <diwic at ubuntu.com <mailto:diwic at ubuntu.com>
>>         > <mailto:diwic at ubuntu.com> <mailto:diwic at ubuntu.com>> wrote:
>>         >
>>         > This is probably a previously asked question, but I
>>         couldn't find
>>         > it on
>>         > Google, so...
>>         >
>>         > Let's extend the Circle example from the guide a little:
>>         >
>>         > struct Circle {
>>         > x:f64,
>>         > y:f64,
>>         > radius:f64,
>>         > }
>>         >
>>         > trait HasArea {
>>         > fn area(&self)-> f64;
>>         > }
>>         >
>>         > impl HasArea for Circle {
>>         > fn area(&self)-> f64 {
>>         > std::f64::consts::PI * (self.radius * self.radius)
>>         > }
>>         > }
>>         >
>>         > struct Pancake {
>>         > circle: Circle,
>>         > is_tasty: bool,
>>         > }
>>         >
>>         >
>>         > ...now, what is the easiest way I can implement HasArea for
>>         > Pancake? I
>>         > could do this:
>>         >
>>         > impl HasArea for Pancake {
>>         > fn area(&self) -> f64 { self.circle.area() }
>>         > }
>>         >
>>         > ...but that means a lot of boiler-plate code, especially if
>>         > HasArea has
>>         > many methods. Hopefully rust will just inline/optimise the
>>         > redirection
>>         > away in most cases to avoid the runtime cost, but is there a
>>         > smarter or
>>         > more idiomatic way of doing this?
>>         >
>>         > // David
>>         >
>>         > _______________________________________________
>>         > Rust-dev mailing list
>>         > Rust-dev at mozilla.org <mailto:Rust-dev at mozilla.org>
>>         > https://mail.mozilla.org/listinfo/rust-dev
>>         >
>>         >
>>
>>
>
>
>     _______________________________________________
>     Rust-dev mailing list
>     Rust-dev at mozilla.org <mailto:Rust-dev at mozilla.org>
>     https://mail.mozilla.org/listinfo/rust-dev
>
>

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/rust-dev/attachments/20141017/131087f0/attachment.html>


More information about the Rust-dev mailing list