-
-
Notifications
You must be signed in to change notification settings - Fork 3.9k
Implement Label traits for their boxed variants #2505
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
@@ -105,7 +105,7 @@ impl Schedule { | |||
.stage_order | |||
.iter() | |||
.enumerate() | |||
.find(|(_i, stage_label)| &***stage_label == target) | |||
.find(|(_i, stage_label)| stage_label.dyn_clone() == target.dyn_clone()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
DynEq
won't work when comparing Box<dyn StageLabel>
and &dyn StageLabel
otherwise. I wasn't able to find any better way of fixing this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the dyn_clone
may add some overhead, but it shouldn't be much as labels probably won't be big, and it's only during stage setup
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you add a test somewhere that would fail with a Box<dyn StageLabel>
without this change? As it doesn't fail on compile, it would make sure we don't change back later on
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This could work for the comparison:
.find(|(_i, stage_label)| stage_label.as_ref() == target)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
.find(|(_i, stage_label)| stage_label.dyn_clone() == target.dyn_clone()) | |
.find(|(_i, stage_label)| stage_label.as_ref() == target) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've added the tests. Apologies for taking so long to do it. :)
@blaind, unfortunately, your suggestion didn't work :( It definitely looks cleaner, but my tests fail if I try it this way.
CI error is an ICE on nightly from wgpu
|
abce55b
to
b56121f
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Useful, tested and straightforward. This looks good to me. We can do this later for system labels too as the need arises.
The downside here is that it seems like this would allow pub fn add_stage_before<S: Stage>(
&mut self,
target: &dyn StageLabel,
label: &dyn StageLabel,
stage: S,
) -> &mut Self { Then cloning when ownership is needed. Requiring references here seems like a net loss in UX relative to nested boxes. |
Agreed. Nested boxes is an obvious code smell, so I'm not too concerned about that. The ergonomics loss is more important. It would be nice to have the same change made for the other label types, but it's not immediately clear that this PR is the place to do it. |
In this case, the nested boxing isn't particularly obvious. The type is never written out and the boxing happens internally on a method that accepts a Imagine a worst case scenario where you write logic that moves stages around on every frame, cloning and reusing the label on each frame. You would have nested boxing on a level that should never be allowed. |
@cart what if we change our APIs to use |
|
Hey @cart, I agree, allowing
|
Omg, they do point to different addresses! I shouldn't write anything in GitHub right before going to sleep. :D |
b56121f
to
a72ab9f
Compare
Hi @cart. I think I was able to confirm that wrapping into nested Note the cursed conversion from fn test_adding_after_boxed_stage() {
let mut schedule = Schedule::default();
let a: &'static str = "first";
schedule.add_stage(a, SystemStage::single_threaded());
let stage = schedule.iter_stages().next().unwrap().0.dyn_clone();
let stage2 = stage.dyn_clone();
let s = Box::new(a);
let ss = Box::new(a) as Box<dyn StageLabel>;
println!("str addr (way 1): {:p}", a);
println!("str addr (way 2): {:p}", (&s as &'static str as *const str).to_raw_parts().0);
println!("box addr (way 1): {:p}", (&ss as &dyn StageLabel as *const dyn StageLabel).to_raw_parts().0);
let ss_ptr = &ss as *const _ as usize;
println!("box addr (way 2): 0x{:x}", ss_ptr);
std::mem::forget(ss);
let ss: Box<&'static str> = unsafe { std::ptr::read(ss_ptr as *const Box<&'static str>) };
println!("str addr (way 3): {:p}", (&ss as &'static str as *const str).to_raw_parts().0);
println!("stage box addr (way 1): {:p}", (&stage as &dyn StageLabel as *const dyn StageLabel).to_raw_parts().0);
let stage_ptr = &stage as *const _ as usize;
println!("stage box addr (way 2): 0x{:x}", stage_ptr);
std::mem::forget(stage);
let stage: Box<&'static str> = unsafe { std::ptr::read(stage_ptr as *const Box<&'static str>) };
println!("stage value addr: {:p}", (&stage as &'static str as *const str).to_raw_parts().0);
println!("cloned stage box addr (way 1): {:p}", (&stage2 as &dyn StageLabel as *const dyn StageLabel).to_raw_parts().0);
let stage2_ptr = &stage2 as *const _ as usize;
println!("cloned stage box addr (way 2): 0x{:x}", stage2_ptr);
std::mem::forget(stage2);
let stage2: Box<&'static str> = unsafe { std::ptr::read(stage2_ptr as *const Box<&'static str>) };
println!("cloned stage value addr: {:p}", (&stage2 as &'static str as *const str).to_raw_parts().0);
} |
a72ab9f
to
2bad81e
Compare
I've made a little playground reproduction that should be helpful for experiments and explanations. |
|
Arg sorry you already did the Label impl |
Boxing a box is very possible with this style of api: dyn_clone() can be implemented in a way to avoid the double boxing. However double-boxing can still occur if you call @BoxyUwU called out that we could But this lack of real type safety still gives me pause. People generally assume they can safely box traits. |
647794f
to
2166d5d
Compare
2166d5d
to
7db5610
Compare
Good call @cart! I've added the wrapper type, which hopefully will help to avoid passing Also, unfortunately, it's impossible to make the I get the type safety concern so I'll understand if this won't be merged. But having said that I'd still be glad to see it released, as it unblocks me from using the |
Could we make it |
What's the status of this now that #4957 has been merged? |
This can be closed out; we can make an equivalent PR if the need arises. |
The use-case that I couldn't achieve with the current API of Bevy was iterating over all existing stages and inserting additional stages that would execute before or after the iterated ones.
Here's, for instance, the code that I wrote to measure stages execution time with Puffin that requires this