rust: sharing type definitions across multiple interfaces
Consider the following WIT file:
package test:component;
interface i {
resource r;
enum kind { a, b, c }
f: func(k: kind) -> r;
}
// wrapped-i is exactly the same as i
interface wrapped-i {
resource r;
enum kind { a, b, c }
f: func(k: kind) -> r;
}
world root {
import i;
import wrapped-i;
export i;
export wrapped-i;
}
In wit-bindgen, kind will be defined four times: test::component::i::Kind, test::component::wrapped_i::Kind, exports::test::component::i::Kind, and exports::test::component::wrapped_i::Kind. It would be good to have an option to say that these four kind are the same, and we only define it once, the remaining module will use type alias instead. This avoids value cloning when we want to pass kind from one interface to another. This only works for types which contain no resources. With the lazy value proposal, this may change though.
I can see three options:
-
add a
interface_groupflag, so that we can sayimport.i,import.wrapped-i,export.i,export.wrapped-iare in the same group, and the bindgen will find all non-resource types in the interface and make sure they are defined only once. -
We could also fix this by extending the WIT syntax, something like
world root {
import i;
import i as wrapped-i;
export i;
export i as wrapped-i
}
Then wit-bindgen will know that the non-resource types in these four interfaces are the same just by looking at the WIT file.
- add a
type_eqflag, and sayimport.i.kind,import.wrapped-i.kind,export.i.kindandexport.wrapped-i.kindare equal. This allows more fine grained control, and can handle cases where the two interfaces are not exactly the same, but we still want to share some data types.
It feels like that the interface level equality is better fixed from the WIT syntax, but the type level equality is a suitable extension in wit-bindgen alone and can also cover the interface equality case without waiting for a syntax change.
I agree it'd be nice to solve this! I was talking to folks in Munich recently and this came up as something that would be very nice there too, so you're definitely not alone.
Personally I'd prefer for this to be automatic rather than requiring an opt-in and/or configuration. I think it'd be relatively reasonable to as it's a somewhat-simple hash/equality function of a type. For example records-with-resources are all the same if the resources themselves are the same, so there's no need to handle resources separately per se.
Regardless though I do think that this will be nontrivial to implement in the generator. Definitely very nice to have though!
I think it'd be relatively reasonable to as it's a somewhat-simple hash/equality function of a type.
That's an interesting idea! So we need to look for structural equality for non-resource types and nominal equality for resource types. But then do we consider enum kind1 { a, b, c } and enum kind2 { a, b, c } to be the same type?
records-with-resources are all the same if the resources themselves are the same, so there's no need to handle resources separately per se.
Yeah, this works for i.r and wrapped-i.r, because they come from different places. But for import i; export i;, wit-parser will treat i.r to be the same, because they have the same interface id. Probably need to differentiate import and export interfaces in resolve somehow.
I do think that this will be nontrivial to implement in the generator
If we manually specify the equal types, I think the implementation is actually simple. We can reuse the with flag. When one of the equal types is defined, add that type name to the with map, so that future equal types will use the generated type from with instead.
But then do we consider enum kind1 { a, b, c } and enum kind2 { a, b, c } to be the same type
I think that would fall out of this scheme yeah, but that's also ok and to some degree expected since that's what the CM specifies as well (they're equivalent types)
Agreed also yeah import/export of the same resource needs special care. It may mean the "key" for a resource is not just its TypeId but also a bool if it's exported or imported.