We went through a somewhat similar process at an old job. Entities don't have an ID before they've been saved, but do after. Other things can change over their lifecycle too. At one point, we had separate types for each stage in the lifecycle, a bit like your different types for insert and read, but that makes it impossible to write any kind of code which is stage-agnostic - so for example you need a separate to-string function for each stage.
We were working in Scala, and I had a go at solving this using a higher-kinded type for the ID and other changing fields:
https://github.com/tim-group/higher-kinded-lifecycle/blob/ma...
The idea is that if you say that the ID is of type 'T of String, where T is some subtype of Option', then you can bind T to None for entities that haven't been saved, Some for ones which have, and Option where you have a mixture. You then have one type definition, with which you can write stage-agnostic code, but static guarantees about whether there's an ID.
You can go further and use type constraints to enforce the order of lifecycle stages: if your object can only be saved to the database once it's been given a timestamp, say, you can make sure that the ID type can only be Some if the timestamp type is also Some.
So maybe when Rust gets HKTs it'll be time for a new release!