Swift struct extension & AssociatedObject

Hi guys,
I have two questions about struct

  • Question1 extension
struct Test {
    func test() { }
}

extension Test {
    // compile error: Invalid redeclaration of 'test()'
    func test() { }
}

extension Array {
    // compile successfully
    func append(_ newElement: Element) { }
}

Code shows as above. Array is also a struct, why our custom struct compile failed?

  • Question2 AssociatedObject
var list = [String]()
var listKey: UInt8 = 0
// Array is a struct, and we can get the associated object
objc_setAssociatedObject(list, &listKey, "Associated String", .OBJC_ASSOCIATION_COPY_NONATOMIC)
objc_getAssociatedObject(list, &listKey ) // => return "Associated String"

var test = Test()
var testKey: UInt8 = 1
// Test is a custom struct which we defined above, and this time we cannot get the associated object, it returns nil
objc_setAssociatedObject(test, &testKey, "Associated String", .OBJC_ASSOCIATION_COPY_NONATOMIC)
objc_getAssociatedObject(test, &testKey ) // => return nil

Could anyone kindly help me? I know maybe those two question are stupid.
BR

Regarding your first question. Append is a 'customization point' in a RangeReplaceableCollection protocol with default implementation in in protocol's extension. It's as if you did something akin to that:

struct Foo {}

protocol Bar {
    func doStuff()
}

extension Bar {
    func doStuff() {
        print("did stuff")
    }
 }

extension Foo: Bar {}

extension Foo {
    func doStuff() {
        print("did other stuff")
    }
}
1 Like

I think it's belong to Paradigm Programming, [Int] and [Any] are two different types. So they can be defined two same method names.

I found some code from ArrayProtocol

Struct Source Code (Just like Array Source Code)

struct Foo<Bar> {}

protocol P {
    associatedtype A
    var foo: Foo<A> { get }
    func method()
}

extension Foo: P where Bar: P {
    typealias A = Bar
    
    var foo: Foo { return self }
    
    func method() {
        print("method1")
    }
}

Extension For Struct (Just like Extension For Array)

extension Foo where Bar == String {
   func method() {
         print("method2")
   }
}

Next Run Test Code

let foo = Foo<String>()
foo.method()    // print method2

let foo2 = Foo<Int>()
foo.method()   // compile failed

struct p: P {
    typealias A = Any
    var foo: Foo<Any>
    func method() {
        print("method3")
    }
}
let foo3 = Foo<p>()
foo3.method()   //  compile success and print method1, Because of the following code(extension Foo: P where Bar: P {})

The Array Extension Element is Any (compile failure bacause of defined the same method with Array source sode)

None of these are quite the correct answer for Question 1 (sorry).

Swift allows you to declare methods in an extension that conflict with another module's methods in case you were there first. If the standard library didn't have append and then added it, the meaning of your own code shouldn't change. [EDIT: As @TannerJin points out, though, it doesn't always work.]

Unfortunately, this breaks down a little when you start getting into multiple modules (there's no longer a clear choice of which one you meant), and it's questionable if it's even a good idea within one module, since it means your code looks standard but does something different.


For Question 2, Swift will convert values to Objective-C objects when asked to, but it doesn't guarantee whether it will convert it to the same instance or make a new instance each time. It is therefore not safe to use associated objects with structs (or enums, or anything except class instances), even though the compiler will allow you to do it and it appears to work in the array case.

7 Likes

Tanks a lot!

Your answer makes sense for me. Tanks for your effort.