-
Notifications
You must be signed in to change notification settings - Fork 137
Change UserState to outside of GraphEditorState #58
Conversation
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.
Thanks for the PR! 😄 Everything looks good, sorry it took me a while to review this.
I left a couple of questions just to make sure I understand the changes, but I don't think there's any actionable stuff.
@@ -29,13 +30,13 @@ pub struct GraphEditorState<NodeData, DataType, ValueType, NodeTemplate, UserSta | |||
pub node_finder: Option<NodeFinder<NodeTemplate>>, | |||
/// The panning of the graph viewport. | |||
pub pan_zoom: PanZoom, | |||
pub user_state: UserState, | |||
pub _user_state: PhantomData<fn() -> UserState>, |
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'm not an expert with the rules about variance. Usually I've seen fn() -> T
used as the PhantomData
argument when you want to force the two types to be invariant. Why is invariance important here?
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.
Thank you for your review!
The conclusion is that the impact of T
can be reduced.
First, to correct you, according to The Rustonomicon fn() -> T
is covariant.
Second, I quote from rust-lang/rust#46749 (comment)
PhantomData does 3 things:
- variance
- owns
- auto trait opt outs
The key here is "owns" and "auto trait opt outs".
An example of "auto trait opt outs", For example, with a code like this:
use std::{marker::PhantomData, rc::Rc, thread};
#[derive(Debug, Default)]
struct Wrapper<T>{
_phantom: PhantomData<T>
}
fn main() {
let out = Wrapper::<Rc<usize>>::default();
thread::spawn(move|| out);
}
Run in Rust Playground
This code can't be compiled, because Rc<usize>
and Outer<Rc<usize>>
are not impled Send
.
PhantomData<T>
inherits !Send
.
How to fix this:
use std::{marker::PhantomData, rc::Rc, thread};
#[derive(Debug, Default)]
struct Wrapper<T>{
_phantom: PhantomData<fn()->T>
}
fn main() {
let out = Wrapper::<Rc<usize>>::default();
thread::spawn(move|| out);
}
Run in Rust Playgound
This doesn't inherit !Send
and can be compiled.
Even if T is !Send
, the Wrapper
is Send
.
Also, regarding "owns", I am not familiar with it, but I heard that PhantomData<fn()->T>
is better than PhantomData<T>
due to DropChekcer (unless you are implementing an unsafe Drop).
egui_node_graph_example/src/app.rs
Outdated
/// If the persistence feature is enabled, Called once before the first frame. | ||
/// Load previous app state (if any). | ||
pub fn new(cc: &eframe::CreationContext<'_>) -> Self { | ||
let state = if let Some(storage) = cc.storage { | ||
eframe::get_value(storage, PERSISTENCE_KEY) | ||
.unwrap_or_else(|| GraphEditorState::new(0.0)) | ||
} else { | ||
GraphEditorState::new(0.0) | ||
}; | ||
Self { | ||
state, | ||
user_state: MyGraphState::default(), |
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.
Nice touch! I like that the example app keeps the state now. One question though: Are we sure this won't panic when the type definitions change? I'm assuming the unwrap_or_else
call would catch 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.
If the field name is different, get_value returns None, but since it is stored in Ron format, the difference between usize
and i32
, etc., seems to be cast.
If you want to include the change discussed in #57 about passing |
I changed the UserState args to mutable and made NodeGraphExample::new() simpler using syntax sugar. |
Looks ready to merge! Thanks again for the PR 😄 |
Changes