Nesting structs in Protocol Extension: Type '...' cannot be nested in generic function '...()' - swift

I have a protocol, let's say Fruit (see below).
Within one of the methods I want to use a custom struct.
This results in the following error:
Type 'Packet' cannot be nested in generic function 'saveObject()'
Why is this not allowed?
protocol Fruit: Codable
{
var vitamines: Int { get }
var description: String { get }
}
extension Fruit
{
func saveObject()
{
struct Packet
{
let val1, val2, val3: Int
}
let packet = Packet(val1: vitamines, val2: 0, val3: 0)
}
}
Looking for a solution or viable alternatives.
I've tried using a tuple but I need to save the Packet to Data, which is not easily possible using a tuple (as far as I know).

You cannot nest new types inside of generic structures in extensions this way. Allowing that would likely become very complex, since it is not quite clear whether this type would be Fruit.saveObject.Packet or <ConformingType>.saveObject.Packet. For example, consider the following (legal) code for how these kinds of types can escape, and the system has to deal with them, including knowing how to dispatch methods to them, how much storage they require, etc.
protocol P {}
func x() -> P {
struct T: P {}
return T()
}
type(of: x())
If you change this to make x() generic, then it is no longer legal:
func x<Y>() -> P {
struct T: P {} // error: type 'T' cannot be nested in generic function 'x()'
return T()
}
That said, if you believe that the language should be changed to allow this, then Swift Evolution is the process to suggest it. You should first think though how you would like this to work if Fruit had associatedtypes, if saveObject() were itself generic, and if Packet included reference to type variable defined in either of those places. (I'm not saying these are insurmountable problems at all. This may be an excellent feature and it may be possible to design it very well. You just need to think through how it interacts with other features of the language.)
The solution is to move Packet to the top level, outside the extension and outside the protocol.

Related

Swift compile time dynamic type property

Is there any syntax can make this work? I need a property can determine its type in the compile time.
protocol P {}
struct A: P {
var onlyAHas: String
}
struct B: P {
var onlyBHas: String
}
var ins1: any P = A()
var ins2: any P = B()
ins1.onlyAHas = "a only"
ins2.onlyBHas = "b only"
Before getting to the solution, let's break down what any means, and while we're at it, we'll include some as well:
When you write:
var ins1: any P = A()
You are telling the compiler that you want to use ins1 as P. It's the protocol oriented equivalent of this OOP code:
class Base {
var baseProperty: String? = nil
}
class Concrete: Base {
var concreteProperty: String? = nil
}
let obj: Base = Concrete();
obj.baseProperty = "Some value" // <-- This is fine
obj.concreteProperty = "Some value" // <-- This is an error
This code tells the compiler that obj is a Base. You can assign it from a Concrete, but because that's a subclass of Base, but obj is still known locally as a Base not as a Concrete, so it can't access the properties of Concrete that weren't inherited from Base.
It's the same in your example. ins1 is known locally as a P not as an A, and P doesn't have an onlyAHas property.
You'd get similar behavior with some instead of any. There are a few differences between the two, but let's just talk about the main one:
some tells the compiler that it will be a type that it can resolve to one specific concrete type, but that it should enforce the abstraction to the protocol in source code. This allows it to generate more efficient code internally, because knowing the concrete type allows the compiler to call the concrete's implementation directly instead of going through its protocol witness table, which is the protocol-oriented analog of a "vtable" in OOP, so the effect is like in OOP when the compiler devirtualizes a method call because despite the syntax, it knows the actual concrete type. This avoids the runtime overhead of dynamic dispatch while still letting you use the abstraction of the existential type... well it's more like it requires you to use the abstraction of the existential type than lets you, because from a source code point of view, the abstraction is enforced.
any also enforces the abstraction, but it goes the other way in terms of the kind of optimizations the compiler can do. It says that the compiler must go through the protocol witness table, because, as the keyword suggests, its value could be any concrete type that conforms to the protocol, even if the compiler could determine that it's actually just one specific type locally. It also allows relaxation of some rules regarding using the protocol as a type when it has Self and associatedtype constraints.
But either way, you are telling the compiler that you want to use ins1 as a P and not as an A.
The solutions
There are a few solutions, actually:
Downcasting
The first is to downcast to the concrete type, as was suggested in comments by Joakim Danielson:
if var ins1 = ins1 as? A {
ins1.onlyAHas = "a only"
}
Downcasting is a code smell, but sometimes is actually the clearest or simplest solution. As long as it's contained locally, and doesn't become a wide-spread practice for using instances of type, P, it might be fine.
However, that example does have one problem: A is a value type, so the ins1 whose onlyAHas property is being set is a copy of the original ins1 you explicitly created. Having the same name confuses it slightly. If you only need the change to be in effect in the body of the if, that works just fine. If you need it to persist outside, you'd have to assign back to the original. Using the same name prevents that, so you'd need to use different names.
Execute concrete-specific code only at initialization
This only applies if the concrete type just configures some things for the protocol up-front, and thereafter protocol-only code can be used:
var ins1: any P = A(onlyAHas: "a only")
// From here on code can only do stuff with `ins1` that is defined in `P`
Or your could delegate the initialization to a function that internally knows the concrete type, but returns any P.
func makeA(_ s: String) -> any P
{
var a = A()
a.onlyAHas = s
return a
}
var ins1 = makeA("a only");
// From here on code can only do stuff with `ins1` that is defined in `P`
Declare protocol methods/computed properties that do the work.
This is the usual way to use protocols. Declaring a method in the protocol is similar to declaring a method in a base class. Implementing the method in a conforming concrete type is like overriding the method in a subclass. If you don't also provide a default implementation in a protocol extension, the protocol will enforce that conforming types implement the protocol - which is a big advantage over the OOP approach.
protocol P {
mutating func setString(_ s: String)
}
struct A: P
{
var onlyAHas: String
mutating func setString(_ s: String) {
onlyAHas = s
}
}
struct B: P
{
var onlyBHas: String
mutating func setString(_ s: String) {
onlyBHas = s
}
}
var ins1: any P = A()
var ins2: any P = B()
ins1.setString("a only") // <- Calls A's setString
ins2.setString("b only") // <- Calls B's setString
I'm doing this with a setString method, but you could certainly use a computed variable in the protocol to do the same thing, and that would be more "Swifty." I didn't do that just to emphasize the more general idea of putting functionality in the protocol, and not get hung up on the fact that the functionality in question happens to be setting a property.
If you don't need all conforming types to be able to set a String, one solution is to provide a do-nothing default implmentation in an extension on P:
protocol P {
mutating func setString(_ s: String)
}
extension P
{
mutating func setString(_ s: String) { /* do nothing */ }
}
// Same A and B definitions go here
struct C: P { }
var ins3: any P = C();
ins1.setString("a only") // <- Calls A's setString
ins2.setString("b only") // <- Calls B's setString
ins3.setString("c only") // <- Calls setString from extension of P
Most often though, setting/getting some concrete property is an implementation detail of doing some task that varies with the concrete type. So instead, you'd declare a method in the protocol to do that task:
protocol P
{
mutating func frobnicate()
}
struct A
{
var onlyAHas: String
mutating func frobnicate()
{
// Do some stuff
onlyAHas = "a only"
// Do some other stuff that uses onlyAHas
}
}
B would be defined similarly doing whatever is specific to it. If the stuff in comments is common code, you could break it down into prologue, main action, and epilogue.
protocol P
{
mutating func prepareToFrobnicate()
mutating func actuallyFrobnicate() -> String
mutating func finishFrobnication(result: String)
}
extension P
{
/*
This method isn't in protocol, so this exact method will be called;
however, it calls methods that *are* in the protocol, we provide
default implementations, so if conforming types, don't implement them,
the versions in this extension are called, but if they do implement
them, their versions will be called.
*/
mutating func frobnicate()
{
prepareToFrobnicate()
finishFrobnication(result: actuallyFrobnicate());
}
mutating func prepareToFrobnicate() {
// do stuff general stuff to prepare to frobnicate
}
mutating func actuallyFrobnicate() -> String {
return "" // just some default value
}
mutating func finishFrobnication(result: String) {
// define some default behavior
}
}
struct A
{
var onlyAHas: String
mutating func actuallyFrobnicate() -> String
{
// Maybe do some A-specific stuff
onlyAHas = "a only"
// Do some more A-specific stuff
return onlyAHas
}
}
struct B
{
var onlyBHas: String
mutating func actuallyFrobnicate() -> String {
"b only"
}
mutating func finishFrobnication(result: String)
{
// Maybe do some B-specific stuff
onlyBHas = result
// Do some more B-specific stuff
}
}
var ins1: any P = A()
var ins2: any P = B()
ins1.frobnicate();
ins2.frobnicate();
In this example, the frobnicate in the protocol extension is called, because it's defined only in the protocol extension.
For ins1, frobnicate then calls the extension's prepareToFrobnicate, because even though it's declared directly in the protocol, A doesn't implement that and a default implementation is provided in the extension.
Then it calls A's actuallyFrobnicate because it's defined directly in the protocol, and A does implement it, so the default implementation isn't used. As a result the onlyAHas property is set.
Then it passes the result from A's actuallyFrobnicate to the finishFrobnication in the extension, because it's defined directly in the protocol, but A doesn't implement it, and the extension provides a default implementation.
For ins2, frobnicate still calls the default prepareToFrobnicate, and then call's B's implementation of actuallyFrobnicate, but B's implementation doesn't set its onlyBHas property there. Instead, it just returns a string, which frobnicate passes to finishFrobnication, which calls B's implementation, because unlike A, B provides its own implementation, and that's where B sets it.
Using this approach, you can simultaneously standardize the general algorithm of a task like frobnicate, while allowing for dramatically different implementation behavior. Of course, in this case, both A and B just set a property in their respective concrete types, but they do it at different phases of the algorithm, and you can imagine adding other code, so that the two effects really would be very different.
The point of this approach is that when we call inst1.frobnicate(), it doesn't know or care about exactly what inst1 is doing internally do accomplish it. The fact that it internally sets the onlyAHas property in the concrete type is an implementation detail the calling code doesn't need to be concerned with.
Just use the concrete type
In your code example, you are creating and using ins1, and ins2 in the same context. So they could just as easily be defined like this:
var ins1 = A()
var ins2 = B()
ins1.onlyAHas = "a only" // <- This is fine because ins1 is an A
ins2.onlyBHas = "b only" // <- This is fine because ins2 is a B
If you have some function, munge that you want to do on both A and B, you can define it terms of the protocol.
func munge(_ p: any P)
{
// In here you can only use `p` as defined by the protocol, `P`
}
If munge needs to do things that depend on concrete-specific properties or methods, you can use one of the previously described approaches...
OR...
If you know for sure that you only will ever have a small number of concrete types conforming to P, which admittedly is sometimes impossible to really know, but occasionally you do, then you can just write specialized overloaded versions of munge for each concrete type:
func munge(_ a: A) {
// Do `A`-specific stuff with `a`
}
func munge(_ b: B) {
// Do `B`-specific stuff with `b`
}
This kind of regresses to older solutions to problems like this. When I say it's an old solution, I'm referring to the fact that even back when the C++ compiler was just a preprocessor that converted C++ source code to C source code which would then be compiled, didn't have templates, and standardization wasn't even on the horizon, it would let you overload functions. You can do that with Swift too, and it's a perfectly valid solution. Sometimes it's even the best solution. More often it leads to code duplication, but it's in your toolbox to use when it's appropriate.

What is the difference between passing classes as generic types or types?

In the Swift documentation, I came across a section which was saying that I can write type constraints like a method or a protocol. However, what I didn't understand were the benefits of writing this way. Why don't simply pass the class (or protocol) itself?
Documentation: https://docs.swift.org/swift-book/LanguageGuide/Generics.html
Let me explain with an example.
import Foundation
class A {
init(val: String) {
self.val = val
}
let val: String
}
func someFunction<T: A>(someT: T) {
print(someT.val)
}
func someFunction2(someT: A) {
print(someT.val)
}
// method 1
someFunction(someT: A(val: "asdasd"))
// method 2
someFunction2(someT: A(val: "asdasd"))
In the snippet below, what is the difference between method 1 and method 2?
In your overly simplified example, there's not much difference.
However, as soon as your type constraints become more complex, the only way to achieve the desired interface is via generics.
A great example if when you want a function to take an input argument whose type is a protocol with an associated type, such as SwiftUI's View.
If you tried to use View as the input argument type, you'd get a compile time error
func modifyView(_ view: View) {
}
Protocol 'View' can only be used as a generic constraint because it has Self or associated type requirements
However, as soon as you make view generic with a type constraint on View, the code compiles.
func modifyView<V: View>(_ view: V) {
}
You don't have to look at custom functions, the standard library is full of generic functions as well. JSONDecoder.decode showcases another common use case for generics, when you want your function to return a specific type based on some input arguments. This enables the developer to only write the function 1x, but make it work on lots of different types, while keeping type safety.
func decode<T: Decodable>(_ type: T.Type, from data: Data) throws -> T

How to do Type erasure in Swift using if-let?

I see many articles on type-erasure. But most of their examples focus on putting different types into an array.
Is there any way I can get this code to work?
protocol A {
associatedtype Data
func printThis(value: Data)
}
class B {
}
let x = B()
if let y = x as? A { // I get error on this line
// Do nothing
}
Xcode error states
Protocol 'A' can only be used as a generic constraint because it has Self or associated type requirements
This example code is just for demonstration purposes.
As of Swift 4, protocols that have associated type requirements can only be used as generic constraints in function declarations, as in:
func foo<T: A>(t: T) where A.Data: Whatever { ... }
Unless you remove the associated type from the protocol, you cannot just type variables to it; you can only use it to define a generic type.
If Swift ever gains the ability to have generalized existentials in the future, then this may change. But for the time being, this just isn't possible in Swift.

Why don't protocols in Swift use brackets instead of associated types? [duplicate]

I'm confused about the difference between the syntax used for associated types for protocols, on the one hand, and generic types on the other.
In Swift, for example, one can define a generic type using something like
struct Stack<T> {
var items = [T]()
mutating func push(item: T) {
items.append(item)
}
mutating func pop() -> T {
return items.removeLast()
}
}
while one defines a protocol with associated types using something like
protocol Container {
associatedtype T
mutating func append(item: T)
var count: Int { get }
subscript(i: Int) -> T { get }
}
Why isn't the latter just:
protocol Container<T> {
mutating func append(item: T)
var count: Int { get }
subscript(i: Int) -> T { get }
}
Is there some deep (or perhaps just obvious and lost on me) reason that the language hasn't adopted the latter syntax?
RobNapier's answer is (as usual) quite good, but just for an alternate perspective that might prove further enlightening...
On Associated Types
A protocol is an abstract set of requirements — a checklist that a concrete type must fulfill in order to say it conforms to the protocol. Traditionally one thinks of that checklist of being behaviors: methods or properties implemented by the concrete type. Associated types are a way of naming the things that are involved in such a checklist, and thereby expanding the definition while keeping it open-ended as to how a conforming type implements conformance.
When you see:
protocol SimpleSetType {
associatedtype Element
func insert(_ element: Element)
func contains(_ element: Element) -> Bool
// ...
}
What that means is that, for a type to claim conformance to SimpleSetType, not only must that type contain insert(_:) and contains(_:) functions, those two functions must take the same type of parameter as each other. But it doesn't matter what the type of that parameter is.
You can implement this protocol with a generic or non-generic type:
class BagOfBytes: SimpleSetType {
func insert(_ byte: UInt8) { /*...*/ }
func contains(_ byte: UInt8) -> Bool { /*...*/ }
}
struct SetOfEquatables<T: Equatable>: SimpleSetType {
func insert(_ item: T) { /*...*/ }
func contains(_ item: T) -> Bool { /*...*/ }
}
Notice that nowhere does BagOfBytes or SetOfEquatables define the connection between SimpleSetType.Element and the type used as the parameter for their two methods — the compiler automagically works out that those types are associated with the right methods, so they meet the protocol's requirement for an associated type.
On Generic Type Parameters
Where associated types expand your vocabulary for creating abstract checklists, generic type parameters restrict the implementation of a concrete type. When you have a generic class like this:
class ViewController<V: View> {
var view: V
}
It doesn't say that there are lots of different ways to make a ViewController (as long as you have a view), it says a ViewController is a real, concrete thing, and it has a view. And furthermore, we don't know exactly what kind of view any given ViewController instance has, but we do know that it must be a View (either a subclass of the View class, or a type implementing the View protocol... we don't say).
Or to put it another way, writing a generic type or function is sort of a shortcut for writing actual code. Take this example:
func allEqual<T: Equatable>(a: T, b: T, c: T) {
return a == b && b == c
}
This has the same effect as if you went through all the Equatable types and wrote:
func allEqual(a: Int, b: Int, c: Int) { return a == b && b == c }
func allEqual(a: String, b: String, c: String) { return a == b && b == c }
func allEqual(a: Samophlange, b: Samophlange, c: Samophlange) { return a == b && b == c }
As you can see, we're creating code here, implementing new behavior — much unlike with protocol associated types where we're only describing the requirements for something else to fulfill.
TLDR
Associated types and generic type parameters are very different kinds of tools: associated types are a language of description, and generics are a language of implementation. They have very different purposes, even though their uses sometimes look similar (especially when it comes to subtle-at-first-glance differences like that between an abstract blueprint for collections of any element type, and an actual collection type that can still have any generic element). Because they're very different beasts, they have different syntax.
Further reading
The Swift team has a nice writeup on generics, protocols, and related features here.
This has been covered a few times on the devlist. The basic answer is that associated types are more flexible than type parameters. While you have a specific case here of one type parameter, it is quite possible to have several. For instance, Collections have an Element type, but also an Index type and a Generator type. If you specialized them entirely with type parameterization, you'd have to talk about things like Array<String, Int, Generator<String>> or the like. (This would allow me to create arrays that were subscripted by something other than Int, which could be considered a feature, but also adds a lot of complexity.)
It's possible to skip all that (Java does), but then you have fewer ways that you can constrain your types. Java in fact is pretty limited in how it can constrain types. You can't have an arbitrary indexing type on your collections in Java. Scala extends the Java type system with associated types just like Swift. Associated types have been incredibly powerful in Scala. They are also a regular source of confusion and hair-tearing.
Whether this extra power is worth it is a completely different question, and only time will tell. But associated types definitely are more powerful than simple type parameterization.
To add to the already great answers, there's another big difference between generics and associated types: the direction of the type generic fulfilment.
In case of generic types, it's the client that dictates which type should be used for the generic, while in case of protocols with associated types that's totally in the control of the type itself. Which means that types that conform to associated types are in liberty to choose the associated type that suits them best, instead of being forced to work with some types they don't know about.
As others have said, the Collection protocol is a good example of why associated types are more fit in some cases. The protocol looks like this (note that I omitted some of the other associated types):
protocol Collection {
associatedtype Element
associatedtype Index
...
}
If the protocol would've been defined as Collection<Element, Index>, then this would've put a great burden on the type conforming to Collection, as it would've have to support any kind of indexing, many of them which don't even make sense (e.g. indexing by a UIApplication value).
So, choosing the associated types road for protocol generics it's also a matter of empowering the type that conforms to that protocol, since it's that type the one that dictates what happens with the generics. And yes, that might sound less flexible, but if you think about it all types that conform to Collection are generic types, however they only allow generics for the types that make sense (i.e. Element), while "hardcoding" the other associated types (e.g. Index) to types that make sense and are usable in their context.

At runtime, how does Swift know which implementation to use?

protocol A {
func f()
}
struct S1 : A {
func f() {
print("S1")
}
}
struct S2 : A {
func f() {
print("S2")
}
}
let array: [A] = [S1(), S2()]
for s: A in array {
s.f()
}
// "S1\n" "S2\n"
If this was an inheritance hierarchy, I would expect Swift to use a v-table to look up the correct implementation. However, the concrete types in array could be anything that implements A, along with any number of other protocols, so how would the Swift runtime know the structure of the object if it was also using v-tables?
The Swift runtime uses a Protocol Witness Table which holds pointers to each type's implementations of the protocol methods.
Mike Ash explains it best in his article Exploring Swift Memory Layout, Part II:
The last one, at offset 32 is a "protocol witness table" for the underlying type and the protocol, which contains pointers to the type's implementations of the protocol methods. This is how the compiler is able to invoke methods, such as p(), on a value of protocol type without knowing the underlying type at runtime.
I would also watch the WWDC video Understanding Swift Performance as suggested in the comments by Hamish.