Skip to content

Separate ErrorKinds by callsite #1342

Description

@LastExceed

Gonna briefly go on a tangent here, but not without reason:
Rust lacks enum variant types. This is a language limitation. If you've used TypeScript before, then you might be familiar with the concept of type unions. If enum variants had dedicated types, then the same variant could be part of multiple enums. The common way to emulate this in Rust is basically:

enum MyEnum {
	Variant1(Variant1),
	Variant2(Variant2),
}

struct Variant1 {
    // ...
}

struct Variant2 {
    // ...
}

Which is painfully verbose, but again: It is a workaround for a language limitation.

Going back to cpal now: If Rust enums were type unions, then our (current) variant types for the ErrorKind enum would be unit structs. But the point here is that having the same variant in 2 enums shouldn't be thought of as having 2 distinct values representing the same thing, but rather as the same type being used in 2 places. The benefit is functional: It tells you which errors can occur at a given callsite. The drawback is an implementation detail. It introduces a lot of boilerplate code because Rust lacks the ability to express type unions concisely.

To get a better feeling for how to weight this tradeoff, I've tried to roughly sketch out the current error landscape:

image

I probably missed a few cases (do let me know), but the general idea would be something like:

Before:

struct Error {
    kind: ErrorKind,
    message: Option<Cow<'static, str>>,
}

enum ErrorKind {
	DeviceBusy,
	DeviceChanged,
	DeviceNotAvailable,
	HostUnavailable,
	InvalidInput,
	PermissionDenied,
	RealtimeDenied,
	ResourceExhausted,
	StreamInvalidated,
	UnsupportedConfig,
	UnsupportedOperation,
	BackendError,
	Other
}

After:

struct Error<Kind> {
    kind: Kind,
    message: Option<Cow<'static, str>>,
}

enum HostErrorKind {
	HostUnavailable,
	General(General)
}

enum DeviceErrorKind {
	DeviceNotAvailable,
	UnsupportedOperation,
    General(General)
}

enum BuildStreamErrorKind {
    DeviceBusy,
    InvalidInput,
	UnsupportedConfig,
    Device(DeviceErrorKind)
}

enum StreamErrorKind {
	StreamInvalidated,
	RealtimeDenied,
    DeviceChanged,
    General(General)
}

enum General {
	PermissionDenied,
	ResourceExhausted,
	BackendError,
	Other
}

Naming TBD

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions