If T is small and cheap to copy (eg i32) then that's probably the best representation. If T is large/expensive to copy, then you could make it Foo<Arc<T>> - ie, make it a requirement in the API that T has a cheap Clone implementation (in time and space). Or just make it use Arc/Rc itself, though that will be much more expensive if T doesn't need it.
Not without some extra indirection. But might as well leave that up to the caller -- if you require T: Clone then people can always pass Arc<Whatever> if they don't want multiple copies.
This also sounds like a problem potentially solved by [internment] (https://crates.io/crates/internment), if you don't mind having an opaque pointer type instead of a u64. Depends on what you're trying to accomplish. Internment works by replacing the u64 with a pointer to T, which further requires a stable heap location for the T.
If you don't need to control the actual u64 values, but just have them as cheap keys, then you might be able to work with usize in an IndexSet. You'll get a usize index from insert_full, or later you can look that up with get_index_of(&T). You can retrieve items with get_index(usize)/[usize] or get(&T).
You could use hashbrown directly to get stable raw_entry, or even use RawTable if you allow unsafe. The split storage is pretty close to how IndexMap works, for another reference.
RawTable::get, RawTable::insert_entry and RawTable::remove_entry looks exactly like what I needed, and they're also safe! Here's the modified version playground link