I've used a normal type parameter for something similar, but it's not exactly ergonomic if you have a bunch of foreign keys in a single struct.
struct User<T> {
id: isize,
post: T,
}
type PostId = isize;
struct Post {
id: isize,
text: String
}
let key_only: User<PostId> = todo!();
let with_post: User<Post> = todo!();
I usually do this with primary keys at least so there's a clear delineation between "saved" and "unsaved" structs, and it works pretty well in that context imo. (Unsaved is Table<()> saved is Table<TableId> and you can use Table<Option<TableId>> if you need to work with both)
Right, this totally works for a simple case like the one in OP.
But it doesn't prevent someone from instantiating User<Vec<usize>>, which doesn't make a lot of sense. Is there a way to restrict generic type to a handful of types? Making User<Post> and User<PostId> valid, but all other combinations invalid?
And I also would be interested in solution that can ergonomically handle many foreign keys in a single struct.
You can use a trait to restrict the valid types fairly easily.
As far as making things ergonomic when there are lots of foreign keys in a table I think you would probably need to fall back on macros.
You could either create a macro that simulates your User<Post = PostId> syntax or one that creates a new struct with the combination of types you want.