Невозможно создать массив типов, соответствующих протоколу в 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 можно использовать только в качестве общего ограничения, поскольку он имеет требования типа Self или связанные с ним.

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

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


person cfischer    schedule 11.11.2014    source источник
comment
возможный дубликат Не удается назначить экземпляр класса его типу протокола?   -  person Antonio    schedule 11.11.2014


Ответы (2)


Скажем, если бы мы могли поместить экземпляр 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())

Теперь мы случайным образом берем foo из foos.

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

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

foo.bar("foo") or foo.bar(1)

Свифт не может.

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

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

Пример:

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
        }
}
person ylin0x81    schedule 11.11.2014

Я играл с вашим кодом, пытаясь понять, как реализовать протокол. Я обнаружил, что вы не можете использовать 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()]
person David Gomez    schedule 11.11.2014
comment
в этом коде BazType - это просто Int, а не тип переменной. - person Edward Ashak; 03.07.2015