Discussion:
[rust-dev] Inheritance / delegation
David Henningsson
2014-10-17 05:56:54 UTC
Permalink
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
Clark Gaebel
2014-10-17 05:59:24 UTC
Permalink
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>
Post by David Henningsson
This is probably a previously asked question, but I couldn't find it on
Google, so...
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
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
https://mail.mozilla.org/listinfo/rust-dev
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/rust-dev/attachments/20141016/e7f6cef8/attachment.html>
David Henningsson
2014-10-17 11:27:53 UTC
Permalink
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?
Post by Clark Gaebel
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
This is probably a previously asked question, but I couldn't find it on
Google, so...
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
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
https://mail.mozilla.org/listinfo/rust-dev
Clark Gaebel
2014-10-17 16:37:47 UTC
Permalink
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>
Post by David Henningsson
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
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
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?
Post by Clark Gaebel
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
This is probably a previously asked question, but I couldn't find it on
Google, so...
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
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
https://mail.mozilla.org/listinfo/rust-dev
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/rust-dev/attachments/20141017/ea6185ca/attachment.html>
David Henningsson
2014-10-17 18:50:59 UTC
Permalink
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?
Post by Clark Gaebel
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
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
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
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?
Post by Clark Gaebel
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
Post by Clark Gaebel
those cases, use an #[inline] annotation.
On Thu, Oct 16, 2014 at 10:57 PM, David Henningsson
<diwic at ubuntu.com
Post by Clark Gaebel
This is probably a previously asked question, but I couldn't find it on
Google, so...
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
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
https://mail.mozilla.org/listinfo/rust-dev
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/rust-dev/attachments/20141017/9042b11b/attachment.html>
Clark Gaebel
2014-10-17 18:57:46 UTC
Permalink
Such a structure should be possible with DST (a very new feature), but I haven?t kept up to date enough to know how to use it properly. In your example, where you just embed a slice instead of having the struct itself dynamically-sized, try:




```

struct Bag {

? price: int,

? items: Vec<int>,

}




enum Shopping {

? InBag(Bag),

? InCart(/* ? */),

}




impl Shopping {

? fn new(price: int, items: Vec<int>) -> Shopping {

? ? InBag(Bag {

? ? ? price: price,

? ? ? items: items,

? ? })

? }

}

```




If you want to avoid the vector, and associated allocation, just use a slice instead. It?ll require some lifetime annotations, though:





```

struct Bag<?a> {

? price: int,

? items: &?a [int],

}




enum Shopping<?a> {

? InBag(Bag<?a>),

? InCart(/* ? */),

}




impl<?a> Shopping<?a> {

? fn new(price: int, items: &?a [int]) -> Shopping<?a> {

? ? InBag(Bag {

? ? ? price: price,

? ? ? items: items,

? ? })

? }

}

```




Regards,

? - Clark

On Fri, Oct 17, 2014 at 11:51 AM, David Henningsson <diwic at ubuntu.com>
Post by David Henningsson
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
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));
}
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.
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?
Post by Clark Gaebel
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
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
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
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?
Post by Clark Gaebel
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
Post by Clark Gaebel
those cases, use an #[inline] annotation.
On Thu, Oct 16, 2014 at 10:57 PM, David Henningsson
<diwic at ubuntu.com
Post by Clark Gaebel
This is probably a previously asked question, but I couldn't find it on
Google, so...
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
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
https://mail.mozilla.org/listinfo/rust-dev
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/rust-dev/attachments/20141017/72d3f094/attachment.html>
Ben Foppa
2014-10-17 19:01:10 UTC
Permalink
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>
Post by David Henningsson
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
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));
}
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.
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?
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>
Post by David Henningsson
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
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
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?
Post by Clark Gaebel
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
This is probably a previously asked question, but I couldn't find it on
Google, so...
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
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
https://mail.mozilla.org/listinfo/rust-dev
_______________________________________________
Rust-dev mailing list
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/c6ff0d08/attachment.html>
David Henningsson
2014-10-17 19:30:07 UTC
Permalink
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.
Post by Ben Foppa
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
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
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));
}
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.
enum Shopping {
InBag(Bag),
InCart( /* ... */ ),
}
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?
Post by Clark Gaebel
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
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
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
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?
Post by Clark Gaebel
impl Pancake {
fn as_circle(&self) -> &Circle { &self.circle }
fn as_mut_circle(&mut self) -> &mut Circle { &mut
self.circle }
Post by Clark Gaebel
}
The compiler will optimize trivial functions, except
cross-crate. In
Post by Clark Gaebel
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>
Post by Clark Gaebel
This is probably a previously asked question, but I
couldn't find
Post by Clark Gaebel
it on
Google, so...
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
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>
Ben Foppa
2014-10-17 19:46:30 UTC
Permalink
As in the declaration, lifetime parameters are template parameters, so
Foo<'a> should be the "default" approach. Using that syntax, references
would I guess technically be &<'a>, but we just use &'a. As for the +, that
confused me when I first saw it too - on the type level, + is dealing with
traits, and I think you'd only see that when you're giving the types
themselves lifetimes. If you see Bar: Foo+'a, Foo is a trait, and Bar is a
type that implements Foo and that exists for at least the given lifetime.

I'm not sure what's going on with your last example - changing the Shopping
struct to be

struct Shopping<'a>(Bag<'a>)

produces the lifetime error, but changing to:

struct Shopping<'a> { bag: Bag<'a> }

doesn't.. That might actually be a bug. You can report it on github!

On Fri, Oct 17, 2014 at 12:30 PM, David Henningsson <diwic at ubuntu.com>
Post by David Henningsson
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.
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" */
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.
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>
Post by David Henningsson
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
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));
}
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.
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?
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>
Post by David Henningsson
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
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
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?
Post by Clark Gaebel
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
This is probably a previously asked question, but I couldn't find it on
Google, so...
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
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
https://mail.mozilla.org/listinfo/rust-dev
_______________________________________________
Rust-dev mailing list
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/551b63a8/attachment.html>
Clark Gaebel
2014-10-17 19:48:33 UTC
Permalink
Oooh fun you?ve hit your first rustc bug! Great!




https://github.com/rust-lang/rust/issues/17178





Niko is already working on this, so a fix is coming soon. Both of those constructions should have failed. The correct way to fix this is:




```

let weights = [ 3,5,7 ];

let b = InBag(Bag { price: 10, weights: weights.as_slice() });

```

On Fri, Oct 17, 2014 at 12:30 PM, David Henningsson <diwic at ubuntu.com>
Post by David Henningsson
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.
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" */
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.
Post by Ben Foppa
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
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
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));
}
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.
enum Shopping {
InBag(Bag),
InCart( /* ... */ ),
}
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?
Post by Clark Gaebel
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
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
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
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?
Post by Clark Gaebel
impl Pancake {
fn as_circle(&self) -> &Circle { &self.circle }
fn as_mut_circle(&mut self) -> &mut Circle { &mut
self.circle }
Post by Clark Gaebel
}
The compiler will optimize trivial functions, except
cross-crate. In
Post by Clark Gaebel
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>
Post by Clark Gaebel
This is probably a previously asked question, but I
couldn't find
Post by Clark Gaebel
it on
Google, so...
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
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/ee5049ab/attachment.html>
Ben Foppa
2014-10-17 19:49:39 UTC
Permalink
Also, re: the lifetimes. I'm not sure what the current status is, but I
know there are some RFCs to make "obvious" lifetimes more inferred.

On Fri, Oct 17, 2014 at 12:48 PM, Clark Gaebel <cg.wowus.cg at gmail.com>
Post by Clark Gaebel
Oooh fun you?ve hit your first rustc bug! Great!
https://github.com/rust-lang/rust/issues/17178
Niko is already working on this, so a fix is coming soon. Both of those
```
let weights = [ 3,5,7 ];
let b = InBag(Bag { price: 10, weights: weights.as_slice() });
```
On Fri, Oct 17, 2014 at 12:30 PM, David Henningsson <diwic at ubuntu.com>
Post by David Henningsson
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.
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" */
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.
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>
Post by David Henningsson
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
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));
}
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.
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?
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>
Post by David Henningsson
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
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
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?
Post by Clark Gaebel
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
This is probably a previously asked question, but I couldn't find
it on
Google, so...
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
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
https://mail.mozilla.org/listinfo/rust-dev
_______________________________________________
Rust-dev mailing list
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/b1f53ce2/attachment.html>
Ben Foppa
2014-10-17 18:50:26 UTC
Permalink
I think there are several things you can do here.

The idea of the DList taking ownership of the pancakes seems a little
bizzare; if it takes ownership of the Pancakes, but only sees them as
circles, then just deconstruct your Pancakes into circles and pass those. I
believe you can even do something like:

fn as_box_circle(self) -> Box<Circle> { box self.circle }

(i.e. have the `self` parameter be moved rather than referenced).

If you want shared ownership (i.e. you still want to use the Pancake and
Circle parts from other areas of the code once the DList has the Circle
parts), use something like an Rc.

If you actually want to be able to use Pancakes as drop-in replacements for
Circles, then I believe you can do what Clark said, and implement
Deref<Circle>, and make your data structure can be a
DList<Box<Deref<Circle>>>. If you still want shared ownership, you can make
a DList<Rc<Deref<Circle>>>, or something like that.

Is any of that what you want?

The last two solutions obviously do incur some runtime cost. If you really
just want a DList that strongly *owns *all the circles (which implies to me
that the circles aren't being touched from anywhere else), maybe don't put
them in your Pancakes in the first place.
Post by David Henningsson
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
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
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?
Post by Clark Gaebel
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
This is probably a previously asked question, but I couldn't find it on
Google, so...
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
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
https://mail.mozilla.org/listinfo/rust-dev
_______________________________________________
Rust-dev mailing list
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/70a4004e/attachment.html>
Continue reading on narkive:
Loading...