I don't really understand why there is a difference between export and interface in .wit. Can anyone explain the thinking behind it? I think all exported functions should be able to be imported by other components, or am I missing something?
As I understand it, interfaces are a definition of a collection of related functions and types. Exports are a claim that a component (which implements the world in which the export is) provides an implementation for that interface (and imports are a claim that it relies on them and requires them to be provided).
So an interface can be used to export the same signature from multiple worlds.
From the docs it seems like to import a function of a component from another component, there needs to be a defined interface.
What does a .wit file with a defined export but no interface do then? Export a non-importable function?
There's _always_ a side importing the interface and a a side exporting it. Often, one of them is the host (e.g. wasmtime), but when composing components for example, one will import the interface which the other exports, so that the former can call the latter.
You're right that in order to use a function from another component, there needs to be an interface, otherwise they won't know how to call each other, exchange data and what the format of it is, that's what the WIT is doing. The need for it makes more sense when you think about the scenario where the two components are built in different languages with different type systems and data representations.
Wit file with just an export will generally be part of a package with other wit files, or references to external definitions of interfaces (like WASI). So that file is just describing the world of the component in terms of existing interfaces.
I am reading this tutorial right now.
If this add:
package docs:adder@0.1.0;
interface add {
add: func(a: u32, b: u32) -> u32;
}
world adder {
export add;
}
Is imported by:
// in the 'calculator' project
// wit/world.wit
package docs:calculator;
interface calculate {
eval-expression: func(expr: string) -> u32;
}
world calculator {
export calculate;
import docs:adder/add@0.1.0;
}
and referenced by:
// Bring the imported add function into scope
use bindings::docs::calculator::add::add;
Why couldn't this add:
package docs:adder@0.1.0;
world adder {
export add: func(a: u32, b: u32) -> u32;
}
be imported by:
// in the 'calculator' project
// wit/world.wit
package docs:calculator;
interface calculate {
eval-expression: func(expr: string) -> u32;
}
world calculator {
export calculate;
import docs:adder/adder@0.1.0;
}
and referenced by:
// Bring the imported add function into scope
use bindings::docs::calculator::adder::add;
package docs:adder@0.1.0;
world adder {
export add: func(a: u32, b: u32) -> u32;
}
Is defining a world, which exports a single function, which isn't part of an interface, I think? You should be able to import add: func
as the counterpart to that, if I imagine things correctly.
I don't think so: "However, to use your component from another component, it must export an interface. "
Hmm, fair enough. I've only ever done this with interfaces, never tried without
Actually I'm now confused as to why this:
import docs:adder/add@0.1.0;
Works at all, I first thought it would import:
interface add {
add: func(a: u32, b: u32) -> u32;
}
However that can't be the case, because then how would it be know which implementation to call? So most likely it's importing the add implementation, from here:
world adder {
export add; // this is imported
}
This would be a bit surprising to me, I would think this would need to be imported as:
import docs:adder/adder/add@0.1.0;
Is the adder world special and because it's the same name as the package name, the repetition of adder can be avoided?
Or are exports in different worlds "global", so this would actually be an invalid .wit file?
package docs:adder@0.1.0;
interface add {
add: func(a: u32, b: u32) -> u32;
}
world adder {
export add;
}
world anotherAdder {
export add;
}
Other folks might know better, but I'm relatively sure import and export without other qualifiers refer to interface names. Fully qualified it's namespace:package/interface@version
, if it's a single word, I'd expect it to be an interface in the same package as wherever the reference is made
As far as I know, the worlds are generally used for binding generation - the code of the component will say "I implement this world", which is short for "these are the things I import/export". At least that's how I think about it
Anyone better informed, please correct my rough understanding :pray:
From that would follow that there can only be a single implementation of an interface. Which would also be surprising to me, can someone else clarify this?
A single implementation of an interface in a single component/world. That would make sense to me. Is that a problem?
That would make sense to me too, however you're saying interfaces definitions are imported directly and not via an implementation, meaning a single implementation per package. How would I import anotheradder.add
if I can only reference the add
interface directly?
If I do this:
package example:adder;
interface add {
add: func(a: u32, b: u32) -> u32;
}
/// An example world for the component to target.
world adder {
export add;
}
world another {
export add;
}
I get this error, so multiple implementations seem possible I'm just a bit lost on how to select a "default" one.
$ cargo component build --release
error: failed to create a target world for package `adder` (/home/luap/Code/add/Cargo.toml)
Caused by:
0: failed to select the default world to use for local target `/home/luap/Code/add/wit`
1: multiple worlds found in package `example:adder`: one must be explicitly chosen
I got it, there is a separate world field in cargo.toml.
Would you mind filling an issue for cargo component to make that error message more actionable? I'd appreciate it!
Sure https://github.com/bytecodealliance/cargo-component/issues/227
Thank you!
Last updated: Dec 23 2024 at 12:05 UTC