Невозможно создать массив типов, соответствующих протоколу в Swift

У меня есть следующий протокол и соответствующий ему класс:

protocol Foo{
    typealias BazType

    func bar(x:BazType) ->BazType
}


class Thing: Foo {
    func bar(x: Int) -> Int {
        return x.successor()
    }
}

Когда я пытаюсь создать массив из foos, я получаю нечетную ошибку:

var foos: Array<Foo> = [Thing()]

Протокол Foo может использоваться только как общее ограничение, поскольку он имеет Собственные или связанные типы.

ОК, поэтому его можно использовать, только если у него есть связанное требование типа (что он делает), но по какой-то причине это ошибка? WTF?!

Я не уверен, что полностью понимаю, что компилятор пытается мне сказать...

Ответы

Ответ 1

Скажем, если бы мы могли поместить экземпляр Thing в массив foos, что произойдет?

protocol Foo {
    typealias BazType

    func bar(x:BazType) -> BazType
}

class Thing: Foo {
    func bar(x: Int) -> Int {
        return x.successor()
    }
}

class AnotherThing: Foo {
    func bar(x: String) -> String {
        return x
    }
}

var foos: [Foo] = [Thing()]

Потому что AnotherThing также соответствует Foo, поэтому мы можем также поместить его в foos.

foos.append(AnotherThing())

Теперь мы произвольно получаем a Foo из foos.

let foo = foos[Int(arc4random_uniform(UInt32(foos.count - 1)))]

и я собираюсь вызвать метод bar, можете ли вы сказать мне, что я должен отправить строку или целое число в bar?

foo.bar("foo") или foo.bar(1)

Swift не может.

Поэтому его можно использовать только как общее ограничение.

Какой сценарий требует такого протокола?

Пример:

class MyClass<T: Foo> {
        let fooThing: T?

        init(fooThing: T? = nil) {
                self.fooThing = fooThing
        }

        func myMethod() {
                let thing = fooThing as? Thing // ok
                thing?.bar(1) // fine

                let anotherThing = fooThing as? AnotherThing // no problem
                anotherThing?.bar("foo") // you can do it

                // but you can't downcast it to types which doesn't conform to Foo
                let string = fooThing as? String // this is an error
        }
}

Ответ 2

Я играл с вашим кодом, пытаясь понять, как реализовать протокол. Я обнаружил, что вы не можете использовать Typealias как общий тип, потому что это просто псевдоним, а не сам по себе. Поэтому, если вы объявляете Typealias вне своего протокола и своего класса, вы можете эффективно использовать его в своем коде без каких-либо проблем.

Примечание. Typealias имеет тип Int в своем объявлении, таким образом вы всегда можете использовать псевдоним вместо типа Int и использовать все связанные с ним методы и функции.

Вот как я делаю это:

typealias BazType = Int

protocol Foo{
  func bar(x:BazType) -> BazType
}

class Thing: Foo {
  func bar(x: BazType) -> BazType {
    return x.successor()
  }
}

let elements: Array<Foo> = [Thing(), Thing()]