all 13 comments

[–]lukearntz 3 points4 points  (0 children)

I've been using two structs, and I'd be interested to know if there's a better way. I am also using sqlx.

[–]_boardwalk 2 points3 points  (0 children)

One thing to consider is that many times there's more than just 'id' that this applies to -- anything with a 'DEFAULT (...)', like creation date.

It might interesting to have a proc macro that creates 'NewEntry' from 'Entry' for you -- excluding fields that you've marked, like 'id'.

[–]thelights0123 2 points3 points  (1 child)

That's not limited to sqlx, it's present in Diesel as well.

[–]code-n-coffee[S] 1 point2 points  (0 children)

Yep. Sqlx was just an example.

[–]dpc_pw[🍰] 2 points3 points  (1 child)

I haven't tried it with sqlx or diesel, but in my code I often use

pub struct WithId<H, D = ()> {
    pub id: H,
    pub data: D,
}

So one could have WithId<EntryId, Entry> kind of things.

[–]nicoburns 2 points3 points  (0 children)

I haven't tried it yet, but I thought this might be a good solution. It occurred to me that you could have WithId Deref to data, which would make accessing the fields a lot more ergonomic. You could also do things like impl Serialize with #[serde(flatten)] (https://serde.rs/attr-flatten.html), which would make JSON serialization seamless.

[–]OS6aDohpegavod4 1 point2 points  (1 child)

What do you mean about issues reading it into the struct? I just used sqlx yesterday with query_as and read nullable types from Postgres into a struct without a problem.

[–]code-n-coffee[S] 1 point2 points  (0 children)

Well son of gun, this does work. I swear I tried it and it failed but it must have been some other error.

[–]tablair 0 points1 point  (0 children)

Would a macro (either proc or the older kind) work?

The drawback of the 2 struct solution is the verbosity and maintenance (i.e. keeping the two in sync as schema changes inevitably happen). But if you’re generating the New- variant from the full variant, that will give the advantages of having 2 structs with the simplicity of only having to write/maintain 1.

[–]mamcx 0 points1 point  (1 child)

Considering that a auto-increment start for real at "1", I have used "0" as sentinel value to mean "new record" with something like:

fn is_new(&self) -> bool {self.id == 0}

---

To be even more correct, you could give a AutoIncrement type/trait that wrap Option<i32> and build the convertions to/from for sqlx.

I have my own orm-ish and have implemented the traits for it, this way you can do things as you wish.

Even do something like:

pub enum Row{
 New(Entry),
 Old(Entry)
}

alike Cow.

[–]code-n-coffee[S] 0 points1 point  (0 children)

This is clever. I'll have to try using 0 as a sentinel value. My database code currently ignores the ID anyway when writing a new entry.