Idiomatic way to structure Rust traitHow 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
What are these boxed doors outside store fronts in New York?
Do I have a twin with permutated remainders?
Today is the Center
Why is Minecraft giving an OpenGL error?
Did Shadowfax go to Valinor?
If human space travel is limited by the G force vulnerability, is there a way to counter G forces?
Doing something right before you need it - expression for this?
Are the number of citations and number of published articles the most important criteria for a tenure promotion?
How is it possible to have an ability score that is less than 3?
LaTeX: Why are digits allowed in environments, but forbidden in commands?
How can I make my BBEG immortal short of making them a Lich or Vampire?
Why is 150k or 200k jobs considered good when there's 300k+ births a month?
tikz convert color string to hex value
Can a vampire attack twice with their claws using Multiattack?
Is it possible to run Internet Explorer on OS X El Capitan?
NMaximize is not converging to a solution
Maximum likelihood parameters deviate from posterior distributions
Do infinite dimensional systems make sense?
Why do I get two different answers for this counting problem?
What is the word for reserving something for yourself before others do?
Why are electrically insulating heatsinks so rare? Is it just cost?
Character reincarnated...as a snail
What does the "remote control" for a QF-4 look like?
Why can't I see bouncing of a switch on an oscilloscope?
Idiomatic way to structure Rust trait
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