Step 1 of 5 · Reading · ~3 min
Learn
Builders and Opaque Types
some declares an opaque return type: "this returns one specific type that satisfies this protocol, and I am not telling you which one."
protocol Shape {
func area() -> Double
}
struct Circle: Shape {
let radius: Double
func area() -> Double { return 3.14159 * radius * radius }
}
func makeShape() -> some Shape {
return Circle(radius: 5)
}
print(makeShape().area()) // 78.53975
The caller gets the Shape interface and nothing more. The compiler, though, still knows it is a Circle, and that asymmetry is the whole point.
Opaque is not the same as hidden-at-runtime
Returning a bare protocol type instead boxes the value in an existential container: the concrete type is erased, calls dispatch dynamically through a witness table, and the compiler can neither inline nor specialize. some Shape keeps the identity at compile time, so dispatch stays static and the value keeps its size and layout.
The consequence learners hit first is type identity. Two calls to a function returning some Shape are guaranteed to return the same type, so the results are comparable and can be put in the same array. Two calls returning a boxed protocol carry no such guarantee.
The one-concrete-type rule
A function returning some Protocol must return exactly one concrete type on every path:
func pick(_ big: Bool) -> some Shape {
if big { return Circle(radius: 9) }
return Square(side: 1) // error: branches return different types
}
If you genuinely need to choose the type at runtime, you need an existential instead. On a current toolchain that is spelled any Shape; on this course's grader (Swift 5.2) the any spelling does not exist yet — any is not a keyword there and the line fails with "use of undeclared type 'any'". The 5.2 way to write the same thing is the bare protocol name, -> Shape.
Why SwiftUI needs it
struct ContentView: View {
var body: some View {
VStack {
Text("Hello")
Button("Tap") { }
}
}
}
The real type of that body is a deeply nested generic — VStack<TupleView<(Text, Button<Text>)>> — that no one wants to write and that changes whenever you edit the body. some View lets the compiler keep the exact type for specialization while you write three words.
Choosing between them
some Protocol | existential (Shape here, any Shape on 5.6+) | |
|---|---|---|
| Concrete type | fixed, one per function | any conformer, chosen at runtime |
| Dispatch | static, specializable | dynamic through a witness table |
| Heterogeneous array | no | yes |
Reach for some by default and fall back to an existential only when you actually need to mix types in one collection or decide the type at runtime.
Your exercise
You will return a Square from a function typed -> some Shape and print its area to two decimals. String(format:) is a Foundation API, not a stdlib one, so the file must start with import Foundation — without it the build fails with "incorrect argument labels in call (have 'format:_:', expected 'repeating:count:')", which is the grader's confusing way of saying the Foundation overload was never visible.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…