Protocols
Implementation status: protocol declarations / conformance / witness tables / default implementations / associated types Implemented; Self constraints / protocol extension where Partially implemented; existential types any P GA; class-only protocol Not implemented (use a class constraint)
A protocol defines a capability contract (a list of methods / properties / associated types); types satisfy a protocol through conformance. The SYX witness-table dispatch mechanism is similar to Swift's; user Sequence / Iterator custom for-in is supported (Phase 71).
Protocol Declarations
protocol Describable {
func description() -> String
}
protocol Sequence {
associatedtype Element
func makeIterator() -> Iterator
}
protocol Iterator {
func next() -> Int?
}| Element | Description |
|---|---|
func name(params) -> R | Required method |
var name: T { get } | Required get-only property |
var name: T { get set } | Required get + set |
static func name | Required type method |
init(params) | Required initializer |
associatedtype X | Associated type |
typealias X = Y | Associated type default (Partially implemented) |
Conformance
protocol Named {
var name: String { get }
func describe() -> String
}
class User: Named {
let name: String
init(name: String) { self.name = name }
func describe() -> String { return "User(\(name))" }
}class/struct/enummay all implement protocols- Protocol conformance is declared at a single point within a module (avoiding multi-file conflicts)
Default Implementations
protocol Describable {
func description() -> String
func description() -> String { return "<no description>" } // Default
}
class Foo: Describable {
// Not implementing description() inherits the default
}
class Bar: Describable {
func description() -> String { return "bar" } // Override
}
print(Foo().description()) // "<no description>"
print(Bar().description()) // "bar"Associated Types
protocol Container {
associatedtype Item
func add(_ item: Item)
func get(at: Int) -> Item
}
class IntStack: Container {
typealias Item = Int // Associated type binding
var items: Array<Int> = []
func add(_ item: Int) { items.append(item) }
func get(at i: Int) -> Int { return items[i] }
}Automatic associated-type inference: when the
add/getsignatures uniquely determineItem,typealias Item = …may be omitted.
Protocols as Types
protocol Describable {
func description() -> String { return "witness" }
}
class Foo: Describable {}
let desc: any Describable = Foo()
print(desc.description()) // Dispatched through the witness tableany P is an existential type; dispatch at runtime goes through a witness table. See Chapter 25, Opaque Types.
The Iterator Protocol
Iterator is the core protocol for SYX for-in integration (ADR-223 / M2-S1-01 stable subset):
protocol Iterator {
func next() -> Int?
}- It must have
func next()(missing → S006) - Returning
nilmeans the end
Return type (stable subset): the pinnable subset is
Int?(nilterminates); the genericElement?full type surface remains open. A customclass: Iteratorcan be traversed byfor-in(Phase 71+).
// prints: 6
class Counter: Iterator {
var n: Int
init() { self.n = 0 }
func next() -> Int? {
self.n = self.n + 1
if self.n > 3 { return nil }
return self.n
}
}
func main() {
var s: Int = 0
for x in Counter() { s = s + x }
print(s)
}Phase 71 three-back-end strict: custom
Iteratorfor-inbehaves identically on the VM / unified MIR / WASM back ends. Pinning test:m2_s1_01_collection_sequence_iterator. R-38: this fence is an opt-insy,outputstdout pin (SEM-ITER).
The Sequence Protocol
protocol Sequence {
associatedtype Element
func makeIterator() -> Iterator
}Status:
Sequence.makeIteratoris still partial (thefor-inmain path goes through: Iterator/ built-in Array · String · Range); theElementassociated type is registered. This is notmakeIteratorGA.
Built-in Protocols
| Protocol | Description | Implicit conformance |
|---|---|---|
Equatable | == | Built-in value types |
Hashable | hash(into:) | Built-in value types |
CustomStringConvertible | .description() | Built-in value types |
CustomDebugStringConvertible | Debug description | Built-in value types |
Codable | Serialization | data class + @Codable (L1 GA; L2 partial) |
Iterator | next() -> Int? (subset) | for-in stable subset implemented (ADR-223) |
Sequence / Collection | Iteration / Index | Partially implemented (makeIterator / the full Collection Index surface remains open) |
Sendable | Cross-isolation safety | Explicit annotation |
Error | Error type | User-defined |
Comparable | Ordered comparison | Numeric types GA; custom types need compare |
Defined in crates/syx-stdlib/src/protocols.rs.
Synthesized Protocol Conformance
The @Equatable / @Hashable / @Codable macros synthesize witnesses automatically on data class / associated-value enums:
@Equatable
@Hashable
@Codable
data class Point(x: Int, y: Int)See Chapter 19, Macros.
Protocols as Generic Constraints
func printAll<T: CustomStringConvertible>(items: Array<T>) {
for item in items {
print(item.description())
}
}
printAll(items: [1, 2, 3]) // 1 2 3 (Int: CustomStringConvertible)See Chapter 24, Generics.
Witness Tables
class + protocol → witness table (runtime dispatch):
protocol Named { func name() -> String }
class A: Named { func name() -> String { return "A" } }
class B: Named { func name() -> String { return "B" } }
func greet(_ n: Named) { print("hi \(n.name())") }
greet(A()) // "hi A"
greet(B()) // "hi B"Implementation: the witness table is a hidden field of the class; the
CallWitnessinstruction goes through vtable-like dispatch.
Protocols + actor
An actor may implement a protocol; actor methods are isolated automatically:
protocol Counter {
func increment()
func current() -> Int
}
actor MyCounter: Counter {
var value: Int = 0
func increment() { self.value = self.value + 1 }
func current() -> Int { return self.value }
}
let c: any Counter = MyCounter()
await c.increment() // Crossing isolation requires await
print(await c.current()) // 1Self Constraints
protocol Equatable {
func ==(lhs: Self, rhs: Self) -> Bool
}Self refers to the concrete type implementing the protocol. Status: Partially implemented (basic witness dispatch works).
Protocol Composition
protocol A { func a() }
protocol B { func b() }
func use<T: A & B>(_ x: T) {
x.a()
x.b()
}Status:
&composition constraints are Partially implemented (basicT: A & Bworks); use awhereclause for the full form.
Complete Example
protocol Shape {
func area() -> Double
func describe() -> String
}
class Circle: Shape {
var r: Double
init(r: Double) { self.r = r }
func area() -> Double { return 3.14159 * r * r }
func describe() -> String { return "circle r=\(r)" }
}
class Square: Shape {
var s: Double
init(s: Double) { self.s = s }
func area() -> Double { return s * s }
func describe() -> String { return "square s=\(s)" }
}
func total(_ shapes: Array<Shape>) -> Double {
var sum = 0.0
for s in shapes { sum = sum + s.area() }
return sum
}
func main() {
let shapes: Array<Shape> = [Circle(r: 2.0), Square(s: 3.0)]
for s in shapes { print(s.describe()) }
print("total=\(total(shapes))")
}Existential Array<Shape> witness (R-38 · SEM-EXIST)
Stdout pin matching the R-34 fixture (describe + total * 100):
// prints: circle\nsquare\n2156.636
protocol Shape {
func area() -> Double
func describe() -> String
}
class Circle: Shape {
var r: Double
init(r: Double) { self.r = r }
func area() -> Double { return 3.14159 * self.r * self.r }
func describe() -> String { return "circle" }
}
class Square: Shape {
var s: Double
init(s: Double) { self.s = s }
func area() -> Double { return self.s * self.s }
func describe() -> String { return "square" }
}
func total(_ shapes: Array<Shape>) -> Double {
var sum = 0.0
for s in shapes {
sum = sum + s.area()
}
return sum
}
func main() {
let c = Circle(r: 2.0)
let q = Square(s: 3.0)
let shapes: Array<Shape> = [c, q]
for s in shapes {
print(s.describe())
}
let t = total([c, q])
print(t * 100.0)
}Diagnostics
| Code | Trigger | Fix |
|---|---|---|
| S006 | Iterator missing next() | Add func next() -> Int? (stable subset) |
| S010 | Does not conform to the protocol | Add conformance / implement the method |
| S012 | Associated type error | Add a typealias / check the signature |
| S015 | Hashable inconsistency | Implement hash(into:) / @Hashable |
| S016 | Equatable inconsistency | Implement == / @Equatable |
| P001 | Protocol method signature mismatch | Fix the signature |
Differences from Swift
| Swift | SYX |
|---|---|
| Protocol declaration | Implemented |
| Default implementation | Implemented |
| Associated types | Implemented |
Self constraints | Partially implemented |
class-only protocol | Not implemented (use a class constraint) |
| protocol extension where | Partially implemented |
protocol composition & | Partially implemented |
Existential type any P | Implemented (witness table) |
Opaque return some P | Implemented |
| retroactive conformance | Allowed across modules (unlike Swift) |
Next chapter → Generics
See also → Language Reference: Protocols and Conformances · Chapter 22, Extensions · Chapter 24, Generics · Chapter 25, Opaque Types