feat: add table accessor without lifetime#5055
Conversation
| ctx: std::marker::PhantomData<&'ctx super::RemoteTables>, | ||
| }} | ||
|
|
||
| /// Lifetime-aware accessor marker for the table `{table_name}`. |
There was a problem hiding this comment.
I'm not sure why there are lifetimes on the currently generated code, it doesn't seem like they are necessary 🤔 If someone could give me some more info on that I'd appreciate it!
Nevertheless, I didn't want to introduce breaking changes so this is simply additive. If lifetimes aren't needed in the code, then I'd like to see them eventually removed in the next breaking release.
There was a problem hiding this comment.
Table handles are scoped to the lifetime of the context because (other than the root DbConnection) a context object logcally refers to a single particular database state, and reads through that context object should always return information as of that state. As you've noticed, internally the client cache is a big shared mutable data structure which is not persistent (in the functional data structures sense). So we restrict the lifetime of the table accessors to the scope where that context's state is reflected in the cache.
Description of Changes
Adds a lifetime-aware
TableAccessortrait to the Rust SDK and updates Rust codegen to emit a generated accessor marker type for each table.Generated bindings now include a marker type like:
The existing generated table access methods remain unchanged:
Motivation
Generated table handles are lifetime-scoped to
RemoteTables, which makes it difficult for downstream crates to expose ergonomic generic APIs over generated table accessors. In particular, a method likeRemoteTables::player_positionreturns a handle whose type depends on the borrow lifetime ofRemoteTables, which is hard to represent in higher-order APIs.The existing lifetime is not mechanically required by the current handle storage model: generated table handles own an SDK
TableHandle<Row>, which holds shared connection/cache state rather than borrowing fromRemoteTables. However, the lifetime does express the conceptual scope of a handle relative to the database view that produced it.This PR preserves that scoped API contract instead of removing the lifetime, and adds a generated marker type so generic downstream APIs can name table accessors without erasing or weakening the lifetime relationship.
This enables APIs such as:
instead of requiring closure-based registration everywhere.
API and ABI breaking changes
None.
This is additive and should not break existing generated bindings usage. Existing code such as the below continues to work unchanged:
The new
TableAccessortrait is exported from both the SDK root and__codegen, and generated marker types are added alongside existing generated table handles and access traits.Expected complexity level and risk
Complexity: 2/5
The change is small and additive, but it touches Rust SDK codegen and generated binding shape. The main risk is introducing generated code that depends on the new SDK trait, so generated bindings and the SDK version must match.
This does not alter table handle ownership, callback behavior, or existing access methods.
Testing
cargo check -p spacetimedb-sdk.*TableAccessormarker types.bevy_stdb.cargo check -p bevy_stdbagainst the local SDK.