Idiomatic way to structure Rust trait Announcing the arrival of Valued Associate #679: Cesar Manara Planned maintenance scheduled April 23, 2019 at 23:30 UTC (7:30pm US/Eastern) Data science time! April 2019 and salary with experience The Ask Question Wizard is Live!How to clone a struct storing a boxed trait object?How can you make a safe static singleton in Rust?What is the difference between traits in Rust and typeclasses in Haskell?Issue with Rust using dynamic polymorphism on trait when specifying lifetime on selfImplementing Nested TraitsIdiomatic callbacks in RustIs it possible to implement ASN.1 DER in Rust using Serde?How can I test equality to a given boxed object implementing a trait?Is there any way to simulate Generic Associated Types / Associated Type Constructors in Rust?How to write a trait method taking an iterator of strings, avoiding monomorphization (static dispatch)?Store data that implements a trait in a vector
Does the Rock Gnome trait Artificer's Lore apply when you aren't proficient in History?
Getting representations of the Lie group out of representations of its Lie algebra
Diophantine equation 3^a+1=3^b+5^c
One-one communication
What is "Lambda" in Heston's original paper on stochastic volatility models?
How many time has Arya actually used Needle?
The Nth Gryphon Number
3D Masyu - A Die
Why are two-digit numbers in Jonathan Swift's "Gulliver's Travels" (1726) written in "German style"?
By what mechanism was the 2017 UK General Election called?
Determine whether an integer is a palindrome
Where did Ptolemy compare the Earth to the distance of fixed stars?
As a dual citizen, my US passport will expire one day after traveling to the US. Will this work?
Sally's older brother
French equivalents of おしゃれは足元から (Every good outfit starts with the shoes)
Pointing to problems without suggesting solutions
How to name indistinguishable henchmen in a screenplay?
Plotting a Maclaurin series
Can two people see the same photon?
Why not use the yoke to control yaw, as well as pitch and roll?
How could a hydrazine and N2O4 cloud (or it's reactants) show up in weather radar?
Flight departed from the gate 5 min before scheduled departure time. Refund options
Twin's vs. Twins'
What does 丫 mean? 丫是什么意思?
Idiomatic way to structure Rust trait
Announcing the arrival of Valued Associate #679: Cesar Manara
Planned maintenance scheduled April 23, 2019 at 23:30 UTC (7:30pm US/Eastern)
Data science time! April 2019 and salary with experience
The Ask Question Wizard is Live!How to clone a struct storing a boxed trait object?How can you make a safe static singleton in Rust?What is the difference between traits in Rust and typeclasses in Haskell?Issue with Rust using dynamic polymorphism on trait when specifying lifetime on selfImplementing Nested TraitsIdiomatic callbacks in RustIs it possible to implement ASN.1 DER in Rust using Serde?How can I test equality to a given boxed object implementing a trait?Is there any way to simulate Generic Associated Types / Associated Type Constructors in Rust?How to write a trait method taking an iterator of strings, avoiding monomorphization (static dispatch)?Store data that implements a trait in a vector
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty height:90px;width:728px;box-sizing:border-box;
I have been writing a Ray Caster in Rust following "The Ray Tracer Challenge", and I've been having a hard time figuring out the proper way to implement polymorphism in Rust. My priorities are that the object can be used in a multi-threaded program, and that seems to be the main problem.
I have two cases on this, but I'll focus on one: a shape. There are different kinds of shapes (sticking with the able
suffix I originally called my trait Intersectable
). Here was a working trait object implementation, but it didn't work with multi-threading:
#[derive(Debug)]
pub struct Shape
pub parent: Option<Arc<Shape>>,
pub transform: Matrix4,
pub material: Material,
pub intersectable: Box<Intersectable>,
pub trait Intersectable: Debug + IntersectableClone
fn local_intersect(&self, ray: &Ray, object: Arc<Shape>) -> Vec<Intersection>;
pub trait IntersectableClone
fn clone_box(&self) -> Box<Intersectable>;
impl<T> IntersectableClone for T
where
T: 'static + Intersectable + Clone,
fn clone_box(&self) -> Box<Intersectable>
Box::new(self.clone())
impl Clone for Box<Intersectable>
fn clone(&self) -> Box<Intersectable>
self.clone_box()
#[derive(Clone, Debug)]
pub struct Sphere
impl Intersectable for Sphere
fn local_intersect(&self, ray: &Ray, object: Arc<Shape>) -> Vec<Intersection>
...sphere specific code
#[derive(Clone, Debug)]
pub struct Plane
impl Intersectable for Plane
fn local_intersect(&self, ray: &Ray, object: Arc<Shape>) -> Vec<Intersection>
...plane specific code
As a pure struct, with no official polymorphism, I've written a kind of static dispatch that looks like this:
#[derive(Debug, Clone)]
pub enum IntersectableType
Sphere,
Plane,
#[derive(Debug, Clone)]
pub struct Intersectable
intersectable_type: IntersectableType,
impl Intersectable
pub fn local_intersect(&self, ray: &Ray, object: Arc<Shape>) -> Vec<Intersection>
match self.intersectable_type
IntersectableType::Sphere => self.local_intersect_sphere(ray, object),
IntersectableType::Plane => self.local_intersect_plane(ray, object),
_ => Vec::new(),
fn local_intersect_sphere(&self, ray: &Ray, object: Arc<Shape>) -> Vec<Intersection>
...sphere specific code
fn local_intersect_plane(&self, ray: &Ray, object: Arc<Shape>) -> Vec<Intersection>
...plane specific implementation
This works great, but it feels very non-Rusty. I've hit a few problems with using other implementations:
- Using Box<Intersectable>
(when it was a Trait, not a struct), is difficult to clone (I copied How to clone a struct storing a boxed trait object? but didn't love having to use 'static, since that made concurrency impossible).
- Using Arc<Intersectable>
seemed to have the same problems as Box
, though maybe there is a way to make that work.
Is there a way to do this in Rust that allows me to take advantage of concurrency and not write manual static dispatch like this?
rust
add a comment |
I have been writing a Ray Caster in Rust following "The Ray Tracer Challenge", and I've been having a hard time figuring out the proper way to implement polymorphism in Rust. My priorities are that the object can be used in a multi-threaded program, and that seems to be the main problem.
I have two cases on this, but I'll focus on one: a shape. There are different kinds of shapes (sticking with the able
suffix I originally called my trait Intersectable
). Here was a working trait object implementation, but it didn't work with multi-threading:
#[derive(Debug)]
pub struct Shape
pub parent: Option<Arc<Shape>>,
pub transform: Matrix4,
pub material: Material,
pub intersectable: Box<Intersectable>,
pub trait Intersectable: Debug + IntersectableClone
fn local_intersect(&self, ray: &Ray, object: Arc<Shape>) -> Vec<Intersection>;
pub trait IntersectableClone
fn clone_box(&self) -> Box<Intersectable>;
impl<T> IntersectableClone for T
where
T: 'static + Intersectable + Clone,
fn clone_box(&self) -> Box<Intersectable>
Box::new(self.clone())
impl Clone for Box<Intersectable>
fn clone(&self) -> Box<Intersectable>
self.clone_box()
#[derive(Clone, Debug)]
pub struct Sphere
impl Intersectable for Sphere
fn local_intersect(&self, ray: &Ray, object: Arc<Shape>) -> Vec<Intersection>
...sphere specific code
#[derive(Clone, Debug)]
pub struct Plane
impl Intersectable for Plane
fn local_intersect(&self, ray: &Ray, object: Arc<Shape>) -> Vec<Intersection>
...plane specific code
As a pure struct, with no official polymorphism, I've written a kind of static dispatch that looks like this:
#[derive(Debug, Clone)]
pub enum IntersectableType
Sphere,
Plane,
#[derive(Debug, Clone)]
pub struct Intersectable
intersectable_type: IntersectableType,
impl Intersectable
pub fn local_intersect(&self, ray: &Ray, object: Arc<Shape>) -> Vec<Intersection>
match self.intersectable_type
IntersectableType::Sphere => self.local_intersect_sphere(ray, object),
IntersectableType::Plane => self.local_intersect_plane(ray, object),
_ => Vec::new(),
fn local_intersect_sphere(&self, ray: &Ray, object: Arc<Shape>) -> Vec<Intersection>
...sphere specific code
fn local_intersect_plane(&self, ray: &Ray, object: Arc<Shape>) -> Vec<Intersection>
...plane specific implementation
This works great, but it feels very non-Rusty. I've hit a few problems with using other implementations:
- Using Box<Intersectable>
(when it was a Trait, not a struct), is difficult to clone (I copied How to clone a struct storing a boxed trait object? but didn't love having to use 'static, since that made concurrency impossible).
- Using Arc<Intersectable>
seemed to have the same problems as Box
, though maybe there is a way to make that work.
Is there a way to do this in Rust that allows me to take advantage of concurrency and not write manual static dispatch like this?
rust
Would you be able to show functional examples of the trait approach you've tried so far, so we can understand the issues you had? It'll be a lot easier than us trying to start from scratch.
– loganfsmyth
Mar 9 at 1:25
1
Yes, I have added the main implementation I had before this one!
– Josh
Mar 9 at 1:46
Perhaps you could just store a function pointerfn(&Ray, Arc<Shape>) -> Vec<Intersection>
in your Shape. I believefn
isClone + Send
. It's essentially what you're already doing with dynamic dispatch on empty structs.
– JayDepp
Mar 9 at 2:52
That is a very interesting concept that I hadn't thought about, I will give it a shot and report back!
– Josh
Mar 9 at 6:17
add a comment |
I have been writing a Ray Caster in Rust following "The Ray Tracer Challenge", and I've been having a hard time figuring out the proper way to implement polymorphism in Rust. My priorities are that the object can be used in a multi-threaded program, and that seems to be the main problem.
I have two cases on this, but I'll focus on one: a shape. There are different kinds of shapes (sticking with the able
suffix I originally called my trait Intersectable
). Here was a working trait object implementation, but it didn't work with multi-threading:
#[derive(Debug)]
pub struct Shape
pub parent: Option<Arc<Shape>>,
pub transform: Matrix4,
pub material: Material,
pub intersectable: Box<Intersectable>,
pub trait Intersectable: Debug + IntersectableClone
fn local_intersect(&self, ray: &Ray, object: Arc<Shape>) -> Vec<Intersection>;
pub trait IntersectableClone
fn clone_box(&self) -> Box<Intersectable>;
impl<T> IntersectableClone for T
where
T: 'static + Intersectable + Clone,
fn clone_box(&self) -> Box<Intersectable>
Box::new(self.clone())
impl Clone for Box<Intersectable>
fn clone(&self) -> Box<Intersectable>
self.clone_box()
#[derive(Clone, Debug)]
pub struct Sphere
impl Intersectable for Sphere
fn local_intersect(&self, ray: &Ray, object: Arc<Shape>) -> Vec<Intersection>
...sphere specific code
#[derive(Clone, Debug)]
pub struct Plane
impl Intersectable for Plane
fn local_intersect(&self, ray: &Ray, object: Arc<Shape>) -> Vec<Intersection>
...plane specific code
As a pure struct, with no official polymorphism, I've written a kind of static dispatch that looks like this:
#[derive(Debug, Clone)]
pub enum IntersectableType
Sphere,
Plane,
#[derive(Debug, Clone)]
pub struct Intersectable
intersectable_type: IntersectableType,
impl Intersectable
pub fn local_intersect(&self, ray: &Ray, object: Arc<Shape>) -> Vec<Intersection>
match self.intersectable_type
IntersectableType::Sphere => self.local_intersect_sphere(ray, object),
IntersectableType::Plane => self.local_intersect_plane(ray, object),
_ => Vec::new(),
fn local_intersect_sphere(&self, ray: &Ray, object: Arc<Shape>) -> Vec<Intersection>
...sphere specific code
fn local_intersect_plane(&self, ray: &Ray, object: Arc<Shape>) -> Vec<Intersection>
...plane specific implementation
This works great, but it feels very non-Rusty. I've hit a few problems with using other implementations:
- Using Box<Intersectable>
(when it was a Trait, not a struct), is difficult to clone (I copied How to clone a struct storing a boxed trait object? but didn't love having to use 'static, since that made concurrency impossible).
- Using Arc<Intersectable>
seemed to have the same problems as Box
, though maybe there is a way to make that work.
Is there a way to do this in Rust that allows me to take advantage of concurrency and not write manual static dispatch like this?
rust
I have been writing a Ray Caster in Rust following "The Ray Tracer Challenge", and I've been having a hard time figuring out the proper way to implement polymorphism in Rust. My priorities are that the object can be used in a multi-threaded program, and that seems to be the main problem.
I have two cases on this, but I'll focus on one: a shape. There are different kinds of shapes (sticking with the able
suffix I originally called my trait Intersectable
). Here was a working trait object implementation, but it didn't work with multi-threading:
#[derive(Debug)]
pub struct Shape
pub parent: Option<Arc<Shape>>,
pub transform: Matrix4,
pub material: Material,
pub intersectable: Box<Intersectable>,
pub trait Intersectable: Debug + IntersectableClone
fn local_intersect(&self, ray: &Ray, object: Arc<Shape>) -> Vec<Intersection>;
pub trait IntersectableClone
fn clone_box(&self) -> Box<Intersectable>;
impl<T> IntersectableClone for T
where
T: 'static + Intersectable + Clone,
fn clone_box(&self) -> Box<Intersectable>
Box::new(self.clone())
impl Clone for Box<Intersectable>
fn clone(&self) -> Box<Intersectable>
self.clone_box()
#[derive(Clone, Debug)]
pub struct Sphere
impl Intersectable for Sphere
fn local_intersect(&self, ray: &Ray, object: Arc<Shape>) -> Vec<Intersection>
...sphere specific code
#[derive(Clone, Debug)]
pub struct Plane
impl Intersectable for Plane
fn local_intersect(&self, ray: &Ray, object: Arc<Shape>) -> Vec<Intersection>
...plane specific code
As a pure struct, with no official polymorphism, I've written a kind of static dispatch that looks like this:
#[derive(Debug, Clone)]
pub enum IntersectableType
Sphere,
Plane,
#[derive(Debug, Clone)]
pub struct Intersectable
intersectable_type: IntersectableType,
impl Intersectable
pub fn local_intersect(&self, ray: &Ray, object: Arc<Shape>) -> Vec<Intersection>
match self.intersectable_type
IntersectableType::Sphere => self.local_intersect_sphere(ray, object),
IntersectableType::Plane => self.local_intersect_plane(ray, object),
_ => Vec::new(),
fn local_intersect_sphere(&self, ray: &Ray, object: Arc<Shape>) -> Vec<Intersection>
...sphere specific code
fn local_intersect_plane(&self, ray: &Ray, object: Arc<Shape>) -> Vec<Intersection>
...plane specific implementation
This works great, but it feels very non-Rusty. I've hit a few problems with using other implementations:
- Using Box<Intersectable>
(when it was a Trait, not a struct), is difficult to clone (I copied How to clone a struct storing a boxed trait object? but didn't love having to use 'static, since that made concurrency impossible).
- Using Arc<Intersectable>
seemed to have the same problems as Box
, though maybe there is a way to make that work.
Is there a way to do this in Rust that allows me to take advantage of concurrency and not write manual static dispatch like this?
rust
rust
edited Mar 9 at 1:49
Josh
asked Mar 9 at 1:01
JoshJosh
509
509
Would you be able to show functional examples of the trait approach you've tried so far, so we can understand the issues you had? It'll be a lot easier than us trying to start from scratch.
– loganfsmyth
Mar 9 at 1:25
1
Yes, I have added the main implementation I had before this one!
– Josh
Mar 9 at 1:46
Perhaps you could just store a function pointerfn(&Ray, Arc<Shape>) -> Vec<Intersection>
in your Shape. I believefn
isClone + Send
. It's essentially what you're already doing with dynamic dispatch on empty structs.
– JayDepp
Mar 9 at 2:52
That is a very interesting concept that I hadn't thought about, I will give it a shot and report back!
– Josh
Mar 9 at 6:17
add a comment |
Would you be able to show functional examples of the trait approach you've tried so far, so we can understand the issues you had? It'll be a lot easier than us trying to start from scratch.
– loganfsmyth
Mar 9 at 1:25
1
Yes, I have added the main implementation I had before this one!
– Josh
Mar 9 at 1:46
Perhaps you could just store a function pointerfn(&Ray, Arc<Shape>) -> Vec<Intersection>
in your Shape. I believefn
isClone + Send
. It's essentially what you're already doing with dynamic dispatch on empty structs.
– JayDepp
Mar 9 at 2:52
That is a very interesting concept that I hadn't thought about, I will give it a shot and report back!
– Josh
Mar 9 at 6:17
Would you be able to show functional examples of the trait approach you've tried so far, so we can understand the issues you had? It'll be a lot easier than us trying to start from scratch.
– loganfsmyth
Mar 9 at 1:25
Would you be able to show functional examples of the trait approach you've tried so far, so we can understand the issues you had? It'll be a lot easier than us trying to start from scratch.
– loganfsmyth
Mar 9 at 1:25
1
1
Yes, I have added the main implementation I had before this one!
– Josh
Mar 9 at 1:46
Yes, I have added the main implementation I had before this one!
– Josh
Mar 9 at 1:46
Perhaps you could just store a function pointer
fn(&Ray, Arc<Shape>) -> Vec<Intersection>
in your Shape. I believe fn
is Clone + Send
. It's essentially what you're already doing with dynamic dispatch on empty structs.– JayDepp
Mar 9 at 2:52
Perhaps you could just store a function pointer
fn(&Ray, Arc<Shape>) -> Vec<Intersection>
in your Shape. I believe fn
is Clone + Send
. It's essentially what you're already doing with dynamic dispatch on empty structs.– JayDepp
Mar 9 at 2:52
That is a very interesting concept that I hadn't thought about, I will give it a shot and report back!
– Josh
Mar 9 at 6:17
That is a very interesting concept that I hadn't thought about, I will give it a shot and report back!
– Josh
Mar 9 at 6:17
add a comment |
0
active
oldest
votes
Your Answer
StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");
StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "1"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);
else
createEditor();
);
function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);
);
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f55072976%2fidiomatic-way-to-structure-rust-trait%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
0
active
oldest
votes
0
active
oldest
votes
active
oldest
votes
active
oldest
votes
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f55072976%2fidiomatic-way-to-structure-rust-trait%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Would you be able to show functional examples of the trait approach you've tried so far, so we can understand the issues you had? It'll be a lot easier than us trying to start from scratch.
– loganfsmyth
Mar 9 at 1:25
1
Yes, I have added the main implementation I had before this one!
– Josh
Mar 9 at 1:46
Perhaps you could just store a function pointer
fn(&Ray, Arc<Shape>) -> Vec<Intersection>
in your Shape. I believefn
isClone + Send
. It's essentially what you're already doing with dynamic dispatch on empty structs.– JayDepp
Mar 9 at 2:52
That is a very interesting concept that I hadn't thought about, I will give it a shot and report back!
– Josh
Mar 9 at 6:17