Hi!
I was wondering if this is a problem that could be solved by frunk
- I have many persistent structs which will be receiving "updates" periodically, and I'd like to avoid writing boilerplate for setting each individual field of each struct.
Update transaction would be something like this:
let init = hlist![1, 2, 3];
let update = hlist![None, Option(5), None];
let result; // = ?;
assert_eq!(result, hlist![1, 5, 3]);
#[derive(Generic)]
struct, but that wouldn't be needed and performance isn't key here
Actually looking at this again, I don't think the Semigroup for Option would work here. Maybe you need to define your own newtype with its own Semigroup instance
Maybe something like (not tested..may not compile)
enum Updateable<A> {
NoUpdate,
UpdateWith(A)
}
impl <A> Semigroup for Updateable<A> {
fn combine(&self, other: &Self) -> Self {
// some your update logic here based on type of self and other
}
}
That said, it looks kind of dubious in that it might not follow Semigroup laws, so tread carefully.
thank you for the reply! I think this is similar to what I want, but probably not exactly. Right now I ended up just using a macro surrounding each struct which defines an update
method, it works fairly well.
Main reason I don't think Semigroup would work is the requirement that the update and the updateable have to be the same type - the actual data I'm storing doesn't have a valid None
state - but the updates do.
For instance, a data/update pair might be:
struct Road {
x: i16,
y: i16,
room: RoomName,
next_decay: Option<u64>,
}
struct RoadUpdate {
x: Option<i16>,
y: Option<i16>,
room: Option<RoomName>,
next_decay: Option<Option<u64>>,
}
@lloydmeta (or anyone else), I'm doing some type-level programming and I've encountered a pattern that consistently trips up the compiler, and I'm not sure why. I'm wondering if you might have any insight.
Let's say we define a type-level, non-empty list:
struct COne<H>(PhantomData<H>);
struct CCons<H, T>(PhantomData<(H,T)>);
...and a Contains<E>
trait that's satisfied for that list if E
is in it:
#[marker]
trait Contains<E> {}
/// The needle is the only element of the list.
impl<H> Contains<H> for COne<H> {}
/// The needle is the first element of the list.
impl<H, T> Contains<H> for CCons<H, T> {}
/// The needle is in the tail of the list.
impl<E, H, T> Contains<E> for CCons<H, T>
where T: Contains<E>
{}
This works completely fine. However, if we wrap each of those types in another type:
struct Wrap<T>(T);
/// The needle is the only element of the list.
impl<H> Contains<Wrap<H>> for Wrap< COne<H> > {}
/// The needle is the first element of the list.
impl<H, T> Contains<Wrap<H>> for Wrap< CCons<H, T> > {}
/// The needle is in the tail of the list.
impl<E, H, T> Contains<Wrap<E>> for Wrap< CCons<H, T> >
where Wrap<T>: Contains<Wrap<E>>
{}
...then the compiler fails, overflowing evaluating the requirement Wrap<COne<_>>: Contains<Wrap<_>>
.
Playground link here: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=a6699469d9ef51a0cad5d6f120e35eb5
// mapping over generic representation
let peep = Person {
first_name: "bo",
last_name: "peep",
age: 30,
};
let generic = frunk::into_generic(peep);
// mapping each one
let _ = generic.map(hlist![
|first_name| println!("First name: {}", first_name),
|last_name| println!("Last name: {}", last_name),
|age| println!("age: {}", age),
]);
Added ^ and a LabelledGeneric example to the repo: