Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

WIT type mappings

wit-bindgen-scala maps WIT types in your world to Scala types. Generated imports live under {base-package}.{namespace}.{package}; exports under {base-package}.exports.{namespace}.{package}.

Scalar and composite types

WITScala
boolBoolean
s8Byte
u8scala.scalajs.wit.unsigned.UByte
s16Short
u16scala.scalajs.wit.unsigned.UShort
s32Int
u32scala.scalajs.wit.unsigned.UInt
s64Long
u64scala.scalajs.wit.unsigned.ULong
f32Float
f64Double
charChar
stringString
list<T>Array[T]
option<T>scala.scalajs.wit.Option[T]
result<T, E>scala.scalajs.wit.Result[T, E]
tuple<T1, T2>scala.scalajs.wit.Tuple2[T1, T2]

Unsigned integers use scala.scalajs.wit.unsigned so they can be distinct from signed Scala numeric types at the ABI boundary.

User-defined types

WITScala
recordfinal class with @WitRecord
variantsealed trait with @WitVariant, final class for cases
enumsealed trait with @WitEnum, object for cases
flagsfinal class wrapping Int
resource (import)opaque final class with @WitResourceImport

Imports, exports, and resources

Import function

WIT (world scala imports this interface; see hello component model):

package scala-wasm:rust-compose;

interface greeter {
  greet: func(name: string) -> string;
}

Generated Scala (in the greeter package object under managed sources):

import scala.scalajs.wit.annotation.{WitImport, WitScope, WitName}
import scala.scalajs.wit

@WitImport(
  WitScope.unversioned("scala-wasm", "rust-compose", "greeter"), "greet")
def greet(
  @WitName("name") name: String): String = wit.native

Export function

Same greeter interface, but your world exports it.

WIT:

world my-server {
  export greeter;
}

Stub (scope matches your package and interface):

import scala.scalajs.wit.annotation.{WitExport, WitScope, WitName}
import scala.scalajs.wit

object GreeterExport {
  @WitExport(
    WitScope.unversioned("scala-wasm", "rust-compose", "greeter"), "greet")
  def greet(@WitName("name") name: String): String = s"Hello, $name!"
}

Imported resource

WIT:

interface counters {
  resource counter {
    constructor(initial: s32);
    increment: func();
    get: func() -> s32;
  }
}

Generated Scala:

@WitResourceImport(scope, "counter")
final class Counter private () extends Object {
  @WitResourceMethod("increment")
  def increment(): Unit = scala.scalajs.wit.native

  @WitResourceMethod("get")
  def get(): Int = scala.scalajs.wit.native

  @WitResourceDrop
  def close(): Unit = scala.scalajs.wit.native
}

object Counter {
  @WitResourceConstructor
  def apply(initial: Int): Counter = scala.scalajs.wit.native
}

Use val c = Counter(0); c.increment(); c.get(); c.close(). Resource exports are not generated.

Record, variant, and result

interface catalog {
  record item {
    sku: string,
    tags: list<string>,
  }
  variant money { cash(u32), none }
  enum status { ok, not-found }
  lookup: func(sku: string) -> result<item, status>;
}
@WitRecord(scope, "item")
final class Item(
    @WitName("sku") val sku: String,
    @WitName("tags") val tags: Array[String])

@WitVariant(scope, "money")
sealed trait Money
object Money {
  @WitName("cash")
  final class Cash(@WitName("value") val value: scala.scalajs.wit.unsigned.UInt) extends Money
  @WitName("none")
  object None extends Money
}

@WitEnum(scope, "status")
sealed trait Status
object Status {
  @WitName("ok") object Ok extends Status
  @WitName("not-found") object NotFound extends Status
}

@WitImport(scope, "lookup")
def lookup(@WitName("sku") sku: String): scala.scalajs.wit.Result[Item, Status] =
  scala.scalajs.wit.native

Naming

  • Types: kebab-case → PascalCase
  • Functions and fields: kebab-case → camelCase
  • Packages: kebab-case → snake_case 1
  • Scala keywords: backticks (e.g. `type`)

Generator limits

  • Resource exports are not supported (import only).
  • Futures and streams are not supported yet.

  1. I don't like it, this might change in the future. ↩