-
Notifications
You must be signed in to change notification settings - Fork 4
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
feat: Implement patch for Box and Option #31
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.
The into
transition from patch to instance will confuse some scenarios.
The real behavior is patch_on_default
but not into
If the default is not empty, it will confuse, for example:
/// In s.rs, S is defined
#[derive(Patch)]
struct S {
a: usize,
b: usize,
}
impl Default for S {
fn default() -> Self {
S {
a: 100,
b: 0,
}
}
}
/// In another file, S is used
use s::*;
let patch = SPatch {
a: None,
b: Some(2),
}
let patched_s = patch.into();
/// It is not straightforward,
/// especially in big projects with a lot of developers
/// S { a:100, b: 2, }
I avoided implementing From
trait in this crate because it will easily be misleading. Besides, the from
feature will need Default
trait, and from
is in the default features of this crate, so it will reduce the usability of this crate.
Also, I think the patch_on_default
is still misleading, the following way is good enough and easier to read for another developer.
let patched_s = S::default().apply(patch);
Is it possible to remove the from
feature?
What do you think? I believe less is more. Syntax sugar should fit the needs exactly and save time.
The IMO to create a main struct from his patch, the
I can move the Let me know 😃 |
By the way, I use this implementations in one repo of mine: https://github.com/lenra-io/dofigen/blob/180-extends-files/src/dofigen_struct.rs It might be more explicit with an example |
No, it is not necessary, you can do it by default like you do in from fn from(patch: #name #generics) -> Self {
let mut s = Self::default();
s.apply(patch);
s
} First, it is better to let others know this is from default and then
Renaming the feature does not help, I still think let patched_s = S::default().apply(patch); // No `from` trait
let patched_s: S = patch.into(); // With `from` trait It is not straightforward to use By the way, why do people using the |
The If it's not, I think it's cleaner to keep Keeping both of them would let the dev have different behaviors for the two of them.
Ok, I'll move this in another PR.
Here is the compile error when it's not satisfied: But I'll add some docs, it's always better to add docs, sorry I often forget ^^
I created the |
By the way, this does not work since the |
Okay, my bad, I did not carefully review the previous PR, so I forgot the interface changed. impl ops::Add<SPatch> for S {
type Output = S;
fn add(self, _rhs: SPatch) -> S {
...
}
} Such that we can have
It will be clear and short in one line. |
Nice ! I'll try to implement it. I'll do it in another feature, you'll tell me if we do it as default or not 😃 |
We can make it the default, and we implement both ends of impl ops::Add<S> for SPatch {
type Output = S;
fn add(self, _rhs: S) -> S {
...
}
} If the impl<T, P> Patch<Option<P>> for Option<T>
where
T: Patch<P> + Default,
{
fn apply(&mut self, patch: Option<P>) {
if let Some(patch) = patch {
if let Some(self_) = self {
self_.apply(patch);
} else {
*self = Some(T::default() + patch);
}
} else {
*self = None;
}
}
} And we need Let This also fits your use case. I know this implementation will be harder than you originally proposed, but I still try to avoid using If this is good, could we let |
It's not harder to implement with Using I'll squash the commits to make it cleaner.
Ok, I'll create a specific PR for that 😃 |
Sorry, I think twice. However, I agree this is better with |
IMO the better solution is to have an instance from the patch and that's why we need the ExampleHere is a basic use case that might be clearer. struct User {
name: String,
address: Option<Address>,
}
struct Address {
street: Option<String>,
country: String,
} If we have an initial user like this: let user = User {
name: "Thomas",
address: None,
} If we want to patch the user to set his address (fully or partially) with the next patch: let patch = UserPatch {
address: Some(Some(AddressPatch {
country: Some("France"),
street: None,
}))
} We need to define it from the User {
name: "Thomas",
address: Some(Address {
street: None,
country: "France",
}),
} If we don't, the address won't ever be defined from a patch. Hope it's clearer. |
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.
Hi,
Please give an example under examples.
Let will be 100% clear, and good for other's with the same use case.
Hi @taorepoara , |
Can we do this in the test? #[cfg(feature = "option")]
fn main() {
...
}
#[cfg(not(feature = "option"))]
fn main() { } And put the Could you remove the |
In order to ease the use of standard types like
Box
andOption
I created default implementation of thePatch
trait for them.Each type as is own feature:
Box
:box
Option
:option
I also add a
from_patch
attribute to implementFrom
of the generated patch struct for the main struct.