1- # Opaque Types
1+ # Opaque and Boxed Types
22
33Hide implementation details about a value's type.
44
5- A function or method with an opaque return type
6- hides its return value's type information.
7- Instead of providing a concrete type as the function's return type,
8- the return value is described in terms of the protocols it supports.
5+ Swift provides two ways to hide details about a value's type:
6+ opaque types and boxed protocol types.
97Hiding type information
108is useful at boundaries between
119a module and code that calls into the module,
1210because the underlying type of the return value can remain private.
13- Unlike returning a value whose type is a protocol type,
14- opaque types preserve type identity ---
11+
12+ A function or method that returns an opaque type
13+ hides its return value's type information.
14+ Instead of providing a concrete type as the function's return type,
15+ the return value is described in terms of the protocols it supports.
16+ Opaque types preserve type identity ---
1517the compiler has access to the type information,
1618but clients of the module don't.
1719
20+ A boxed protocol type can store an instance of any type
21+ that conforms to the given protocol.
22+ Boxed protocol types don't preserve type identity ---
23+ the value's specific type isn't known until runtime,
24+ and it can change over time as different values are stored.
25+
1826## The Problem That Opaque Types Solve
1927
2028For example,
@@ -484,24 +492,153 @@ the return value always has the same underlying type of `[T]`,
484492so it follows the requirement that functions with opaque return types
485493must return values of only a single type.
486494
487- ## Differences Between Opaque Types and Protocol Types
495+ ## Boxed Protocol Types
496+
497+ A boxed protocol type is also sometimes called an * existential type* ,
498+ which comes from the phrase
499+ "there exists a type * T* such that * T* conforms to the protocol".
500+ To make a boxed protocol type,
501+ write ` any ` before the name of a protocol.
502+ Here's an example:
503+
504+ ``` swift
505+ struct VerticalShapes : Shape {
506+ var shapes: [any Shape]
507+ func draw () -> String {
508+ return shapes.map { $0 .draw () }.joined (separator : " \n\n " )
509+ }
510+ }
511+
512+ let largeTriangle = Triangle (size : 5 )
513+ let largeSquare = Square (size : 5 )
514+ let vertical = VerticalShapes (shapes : [largeTriangle, largeSquare])
515+ print (vertical.draw ())
516+ ```
517+
518+ <!--
519+ - test: `boxed-protocol-types`
520+
521+ ```swifttest
522+ >> protocol Shape {
523+ >> func draw() -> String
524+ >> }
525+ >> struct Triangle: Shape {
526+ >> var size: Int
527+ >> func draw() -> String {
528+ >> var result: [String] = []
529+ >> for length in 1...size {
530+ >> result.append(String(repeating: "*", count: length))
531+ >> }
532+ >> return result.joined(separator: "\n")
533+ >> }
534+ >> }
535+ >> struct Square: Shape {
536+ >> var size: Int
537+ >> func draw() -> String {
538+ >> let line = String(repeating: "*", count: size)
539+ >> let result = Array<String>(repeating: line, count: size)
540+ >> return result.joined(separator: "\n")
541+ >> }
542+ >
543+ -> struct VerticalShapes: Shape {
544+ var shapes: [any Shape]
545+ func draw() -> String {
546+ return shapes.map { $0.draw() }.joined(separator: "\n\n")
547+ }
548+ }
549+ ->
550+ -> let largeTriangle = Triangle(size: 5)
551+ -> let largeSquare = Square(size: 5)
552+ -> let vertical = VerticalShapes(shapes: [largeTriangle, largeSquare])
553+ -> print(vertical.draw())
554+ << *
555+ << **
556+ << ***
557+ << ****
558+ << *****
559+ <<-
560+ << *****
561+ << *****
562+ << *****
563+ << *****
564+ << *****
565+ ```
566+ -->
567+
568+ In the example above,
569+ ` VerticalShapes ` declares the type of ` shapes ` as ` [any Shape] ` ---
570+ an array of boxed ` Shape ` elements.
571+ Each element in the array can be a different type,
572+ and each of those types must conform to the ` Shape ` protocol.
573+ To support this runtime flexibility,
574+ Swift adds a level of indirection when necessary ---
575+ this indirection is called a * box* ,
576+ and it has a performance cost.
577+
578+ Within the ` VerticalShapes ` type,
579+ the code can use methods, properties, and subscripts
580+ that are required by the ` Shape ` protocol.
581+ For example, the ` draw() ` method of ` VerticalShapes `
582+ calls the ` draw() ` method on each element of the array.
583+ This method is available because ` Shape ` requires a ` draw() ` method.
584+ In contrast,
585+ trying to access the ` size ` property of the triangle,
586+ or any other properties or methods that aren't required by ` Shape ` ,
587+ produces an error.
588+
589+ Contrast the three types you could use for ` shapes ` :
590+
591+ - Using generics,
592+ by writing ` struct VerticalShapes<S: Shape> ` and ` var shapes: [S] ` ,
593+ makes an array whose elements are some specific shape type,
594+ and where the identity of that specific type
595+ is visible to any code that interacts with the array.
596+
597+ - Using an opaque type,
598+ by writing ` var shapes: [some Shape] ` ,
599+ makes an array whose elements are some specific shape type,
600+ and where that specific type's identify is hidden.
601+
602+ - Using a boxed protocol type,
603+ by writing ` var shapes: [any Shape] ` ,
604+ makes an array that can store elements of different types,
605+ and where those types' identities are hidden.
606+
607+ In this case,
608+ a boxed protocol type is the only approach
609+ that lets callers of ` VerticalShapes ` mix different kinds of shapes together.
610+
611+ You can use an ` as ` cast
612+ when you know the underlying type of a boxed value.
613+ For example:
614+
615+ ``` swift
616+ if let downcastTriangle = vertical.shapes[0 ] as? Triangle {
617+ print (downcastTriangle.size )
618+ }
619+ // Prints "5"
620+ ```
621+
622+ For more information, see < doc:TypeCasting#Downcasting > .
623+
624+ ## Differences Between Opaque Types and Boxed Protocol Types
488625
489626Returning an opaque type looks very similar
490- to using a protocol type as the return type of a function,
627+ to using a boxed protocol type as the return type of a function,
491628but these two kinds of return type differ in
492629whether they preserve type identity.
493630An opaque type refers to one specific type,
494631although the caller of the function isn't able to see which type;
495- a protocol type can refer to any type that conforms to the protocol.
632+ a boxed protocol type can refer to any type that conforms to the protocol.
496633Generally speaking,
497- protocol types give you more flexibility
634+ boxed protocol types give you more flexibility
498635about the underlying types of the values they store,
499636and opaque types let you make stronger guarantees
500637about those underlying types.
501638
502639For example,
503640here's a version of ` flip(_:) `
504- that uses a protocol type as its return type
641+ that uses a boxed protocol type as its return type
505642instead of an opaque return type:
506643
507644``` swift
@@ -622,19 +759,19 @@ but adding a `Self` requirement to the protocol
622759doesn't allow for the type erasure that happens
623760when you use the protocol as a type.
624761
625- Using a protocol type as the return type for a function
762+ Using a boxed protocol type as the return type for a function
626763gives you the flexibility to return any type that conforms to the protocol.
627764However, the cost of that flexibility
628765is that some operations aren't possible on the returned values.
629766The example shows how the ` == ` operator isn't available ---
630767it depends on specific type information
631- that isn't preserved by using a protocol type.
768+ that isn't preserved by using a boxed protocol type.
632769
633770Another problem with this approach is that the shape transformations don't nest.
634771The result of flipping a triangle is a value of type ` Shape ` ,
635772and the ` protoFlip(_:) ` function takes an argument
636773of some type that conforms to the ` Shape ` protocol.
637- However, a value of a protocol type doesn't conform to that protocol;
774+ However, a value of a boxed protocol type doesn't conform to that protocol;
638775the value returned by ` protoFlip(_:) ` doesn't conform to ` Shape ` .
639776This means code like ` protoFlip(protoFlip(smallTriangle)) `
640777that applies multiple transformations is invalid
@@ -644,7 +781,7 @@ In contrast,
644781opaque types preserve the identity of the underlying type.
645782Swift can infer associated types,
646783which lets you use an opaque return value
647- in places where a protocol type can't be used as a return value.
784+ in places where a boxed protocol type can't be used as a return value.
648785For example,
649786here's a version of the ` Container ` protocol from < doc:Generics > :
650787
@@ -793,3 +930,4 @@ Licensed under Apache License v2.0 with Runtime Library Exception
793930See https://swift.org/LICENSE.txt for license information
794931See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
795932-->
933+
0 commit comments