feat: add table accessor without lifetime#5055
Conversation
|
Can you give an example of an API that is enhanced by this change? I'm having trouble seeing how this would be useful. |
@gefjon It lets bevy_stdb, and other Rust downstream crates, name a table accessor as a concrete type instead of a per-table closure while keeping the handle's lifetime relationship which is what makes generic table registration possible. The real world outcome is a simpler API for consumers of the Bevy integration: Before .add_table::<Player>(|reg, db| reg.bind(db.player()))After .add_table::<PlayerTableAccessor>()It mainly enables a simpler API and fewer lifetime hoops for integration/tooling authors. So, anyone needing to solve the "register N tables generically" problem rather than calling |
|
how would this work with where clauses tho 🤔 or is that used at a different point where only the row type or smth needs to be known? ^^ |
I'm not exactly sure what you are asking 😅 The benefit of this change is that we can abstract away some of this boilerplate necessary to get:
without the boilerplate in the original snippet. So the marker trait is linked to the row type but also encapsulates how you access the table handle with the proper lifetime. For the where clauses, they're still checked but they just live in two places now. The generated accessor impl enforces them at generation time, and the generic usage points check them at call sites (get method). |
|
I need a way to allow users to "register" which tables they want to bind to before we have a DB connection, and to do that we need a way to get a table handle. This can be done with boilerplate on the consumer side per table, or we can abstract it into the codegen since it is a useful pattern for downstream crates. So before this, we expect the user to pass us the specific table handle method during registration, and now we can just use this marker trait that allows us to do it under the hood generically. Anyway I hope that's helpful/clear. It's a bit specific of a change but has nice consumer side ergonomics, removes some boilerplate, and probably some other useful integration crate functionality (debugging stuff, bulk operations). |
gefjon
left a comment
There was a problem hiding this comment.
Left a suggestion for a revised and expanded doc comment on the new trait. Once that's updated, this should be good to merge.
Co-authored-by: Phoebe Goldman <phoebe@goldman-tribe.org> Signed-off-by: Jeff Rooks <onx2rj@gmail.com>
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 the below instead of requiring closure-based registration everywhere which greatly simplifies downstream crate code and consumer code.
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.