Cannot Assign Class Instance to Its Protocol Type

Cannot assign class instance to its protocol type?

First, you want to read the canonical thread on this from devforums. You particularly want to skip around to read jckarter's comments.

Now to the edited question:

let a = s.shortTrain() //ERROR: Cannot convert the expression's type '()' to type 'Train'

This is because you haven't given the compiler enough information to determine the type of a. Think through what it sees:

func shortTrain<ShortType: Train where ShortType.CarriageType == CarriageType>() -> ShortType {
let a = s.shortTrain()

The compiler needs to figure out the type of a at compile time, and it can't handle abstract types. It needs a fully specified type for ShortType (everything nailed down; all the generics specified, all the type aliases resolved). It looks around, and it sees some constraints on ShortType, but it doesn't see anything that actually gives a type. All it has is a, which isn't giving it any hints.

So unfortunately, that leaves you having to tell it explicitly what you want to happen.

let a: SimpleTrain<String> = s.shortTrain()

That's probably the opposite of what you were going for, but it's about all you can do in Swift right now. The Swift team has indicated (many times) that they are well aware of these issues with associated types (and several other related weaknesses in the type system). They're specifically aware of the Scala type system which can handle these things and has a lot in common with the current Swift type system (though in my experience, getting complex path-dependent associated types to work in Scala can also lead to hair tearing).

That said, it's not exactly obvious from your example what you planned to do with this functionality. Would some Trains return a different type as their shortTrain()?

I find that these problems often blow up in the general case, but tend to be quite solvable in the specific cases for the app in front of you. It's hard to build really arbitrary types in Swift in code that could solve every problem, but it often works out when you focus on the types you'll really need. For example, if shortTrain() returned Self, this obviously gets simpler. If the caller knows the desired resulting type, then an init(shorten:) can probably handle it. A protocol method like shortCarriages() -> [CarriageType] may provide a good bridge. Stay flexible on your design, and one of them will almost certainly work out.

Instantiate class from protocol type

I usually do that by defining a generic method. Try this:

class func createClass<T: CreatableClass>(classType: T.Type) -> CreatableClass {
return classType()
}

Update

A possible workaround is to pass a closure creating a class instance, rather than passing its type:

class ClassCreator {
class func createClass(instantiator: () -> CreatableClass) -> (CreatableClass, CreatableClass.Type) {
let instance = instantiator()
let classType = instance.dynamicType

return (instance, classType)
}
}

let ret = ClassCreator.createClass { ExampleClass() }

The advantage in this case is that you can store the closure in a dictionary for example, and create more instances on demand by just knowing the key (which is something in 1:1 relationship with the class name).

I used that method in a tiny dependency injection framework I developed months ago, which I realized it works only for @objc-compatible classes only though, making it not usable for my needs...

Implement generic protocol method with but use generic for whole class

The type of argument and value are indeed different types. The T generic parameter in foo is just an identifier, and I can change it to anything else:

class MyType<T: FirstProtocol>: SecondProtocol {
var value: T? = nil
func foo<AnythingElse>(argument: AnythingElse) {
// MyType still conforms to SecondProtocol
}
}

The T in foo is a brand new generic parameter, different from the T in MyType. They just so happens to have the same name.

Note that when you declare a generic method, it's the caller that decides what the generic type is, not the generic method. What foo is trying to say here is "I want the T in foo to be the same type as the T in MyType", but it can't say that about its own generic parameters!

One way to fix it is to make SecondProtocol have an associated type:

protocol SecondProtocol {
// name this properly!
associatedtype SomeType: FirstProtocol
func foo(argument: SomeType)
}

class MyType<T: FirstProtocol>: SecondProtocol {
typealias SomeType = T // here is where it says "I want 'SomeType' to be the same type as 'T'!"
var value: T? = nil
func foo(argument: T) {
value = argument
}
}

A protocol is not conforming to a class inherited from a generic parent class?

Because you are doing this in a static function, you can't use self to refer to an instance of your VC. Instead, you have an instance of your VC already! It's vc.

Just set vc as the delegate instead of self:

adapter.delegate = vc

Cannot assign to generic property in protocol

You can do it like this

//: Playground - noun: a place where people can play

import Cocoa

protocol StringIdentifiable {
var id: String? { get }
}

protocol Foo: class {
associatedtype AnyStringIdentifiable: StringIdentifiable
var entity: AnyStringIdentifiable? { get set }
}

protocol Bar {
//var foo: Foo { get set } // can't do because Foo is generic
associatedtype AnyFoo: Foo
var foo: AnyFoo { get set }
}

extension Bar {
func setEntity(_ entity: AnyFoo.AnyStringIdentifiable) {
foo.entity = entity
}
}

Within Bar you can use AnyFoo.AnyStringIdentifiable to make sure the types are correct when setting foo.entity, because foo.entity is of the type AnyFoo.AnyStringIdentifiable.

class instance can not assign to inherited interface

The class ChargingPileCommandCoder implements IProtocolPackage<byte[]> but not IProtocolPackage<byte>.



Related Topics



Leave a reply



Submit