Does anyone use a pattern with traits generated from a specification?

โš“ Rust    ๐Ÿ“… 2026-01-10    ๐Ÿ‘ค surdeus    ๐Ÿ‘๏ธ 2      

surdeus

I'll admit my grasp on this idea is vague, so my terminology is probably off in at least some way. Here's the workflow:

  1. Write a specification in some domain-specific parsable format
  2. Write/run a tool which outputs a Rust file containing translations of any pure data types into structs/enums, and any behaviors as traits that work off of the data types
  3. In another module, implement all of those traits on your own types
  4. Pass the custom types through the generated types to an underlying layer of the application

If implemented properly, I believe this makes it easy to write software that matches a specification, while not compromising on maintainability. I see this mostly being useful for bridging the "design" of an application (in the form of the specification) with a third-party framework.

The pattern itself is abstract, so I'll try to give some more concrete examples of it in hopes of getting the point across more clearly:

Trustfall adapter stubs

Trustfall is a crate for turning "anything" into a database. To use it, one writes a schema (GraphQL-like) and feeds it to the library at runtime, and the library calls back into an Adapter with strings indicating what data to provide. This is a powerful model, but in my opinion the dependence on string-typed data is a hindrance to design and iteration. There's a tool that generates the ideal skeleton automatically, but it's only usable one time (can't update the output without discarding changes).

Instead, imagine that the tool output enums for each of the edge types (where it currently generates string matches inside of various files). Then it output a domain-specific "adapter implementation" trait for users to implement, which has methods for each case that is currently generated as a todo!(). Finally, it would generate a bridge struct implementing the trustfall::provider::Adapter trait in terms of the generated trait to close the gap. Shown in pseudo-types:

// not shown: the actual enums generated by this process
trait AdapterImplementation {
    // methods implemented in terms of the enums, in place of the todo! items
}
struct AdapterBridge<A>(A);
impl<A> trustfall::provider::Adapter for AdapterBridge where A: AdapterImplementation {
    // implements the normal trait in terms of the generated enum
}

I suggested this concept (explaining it even more poorly; I should really stop only doing GitHub while heavily sleep-deprived) last August: feature request/concept: statically-typed+reexecutable alternative to `trustfall_stubgen` ยท obi1kenobi/trustfall ยท Discussion #801 ยท GitHub. Unfortunately, I haven't actually built this yet, but I plan to get there "soon" (for some value of soon).

OpenAPI Spec โ†’ Axum

One complaint I have with how OpenAPI is used in practice is that the majority of the time, the specification is generated from the server code (as documentation) rather than being written as a specification and then implemented on the server. Once again, there's tooling that can bootstrap a server implementation from an OpenAPI specification, but nothing that can do so more than once, so if the specification evolves, nothing is done to update it.

Instead, the specification document could be used to produce a trait which has methods for handling each route, with the appropriate parameters encoded into the trait methods. It could also generate a function that creates a Router from an implementation of the trait. The main issue right now is that a design which allows passing other extractors, such as state, to the handlers is currently left as an exercise for the reader.

Just like for Trustfall, this would allow for statically ensuring that the server endpoints match the specification without losing the ability to freely update the specification itself. It encourages designing a "good" API first without preventing gradual feature addition and modifications. The primary downside for this design is that it strips away Axum's flexibility.


Those two are the main use cases I've considered for this pattern. What do you all think of this as a concept? Does anyone here use or know of a tool that behaves like this?

2 posts - 2 participants

Read full topic

๐Ÿท๏ธ Rust_feed