3

I have a snippet of code like below:

protocol SomeProtocol {
}

struct SomeObject: SomeProtocol {
}

struct Test {

    var arr: [[SomeProtocol]]

    mutating func testFunction(objs:[[SomeObject]]) {
        self.arr = objs
    }
}

As you can see, SomeObject confirm the protocol, but the compiler shows me this error:

cannot assign value of type '[[SomeObject]]' to type '[[SomeProtocol]]'.

Can somebody tell me the reason?

Thanks a lot!

2
  • This could be a similar issue as in stackoverflow.com/questions/34257678/…. Commented Dec 14, 2015 at 8:45
  • 1
    What if you try to cast the array? Anyway it has to do via covariance and controvariance. SomeObject is SomeProtocol but [[SomeObject]] is not [[SomeProtocol]]. Probably you need to cast each element. This was the same in .net 3.5, and it has been "resolved" in .net 4.0. Commented Dec 14, 2015 at 9:09

1 Answer 1

2

Because [[SomeObject]] is not the same type as [[SomeProtocol]] - see @TeeJay's comment above. The how is solved like this:

protocol SomeProtocol {
}

struct SomeObject: SomeProtocol {
}

struct Test {

    var arr: [[SomeProtocol]]

    mutating func testFunction(objs:[[SomeObject]]) {
        self.arr = []
        for a in objs {
            var innerArray = [SomeProtocol]()
            for e in a {
                innerArray.append(e)
            }
            self.arr.append(innerArray)
        }
    }
}

Or, as @MartinR pointed out, you could use map.

If I try to short-circuit the inner loop with

    self.arr = []
    for a in objs {
        self.arr.append(a)
    }

I get the error "Cannot convert value of type 'Array<SomeObject>' to expected argument type [SomeProtocol]", which, on one level, is entirely correct - they are not the same - it's the elements, not the collection, which conform to the protocol. Array assignment doesn't dig down to the final element types for protocol conformance, unless you do it explicitly.

This is not entirely surprising - you wouldn't expect the following to work:

struct SomeOther1 {
    var a : SomeProtocol
}

struct SomeOther2 {
    var a : SomeObject
}

let x = SomeOther2(a: SomeObject())
let y: SomeOther1 = x // ERROR

whilst this should (and does) work:

let x = SomeOther2(a: SomeObject())
let y = SomeOther1(a: x.a)

Since Arrays are Structs that use generics, we can try the same using generics:

struct SomeOther<T> {
    var a: T
}

let z = SomeOther<SomeObject>(a: SomeObject()) // OK, of course
let w = SomeOther<SomeProtocol>(a: SomeObject()) // OK, as expected
let v: SomeOther<SomeProtocol> = z // Doesn't work - types are not the same.
Sign up to request clarification or add additional context in comments.

2 Comments

Yep, it's true I can solve the problem like this, but I want to know why. Anyway, thank you
Reasonably certain of the why now - updated answer.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.