Cannot convert return expression of type "child" to return type T

I struggle a bit with generics at Swift.

I have the following code:

class Parent {
    init(value: SomeThing) {
        // ...
    }

    func clone<T: Parent>() -> T {        
        return T(value: SomeThing)
    }
}

class Child : Parent {
    var otherValue: SomeThingElse?

    override func clone<T>() -> T where T : Parent {        
        let clone: Child = super.clone()
        clone.otherValue = self.otherValue
        return clone  //ERROR: cannot convert return expression of type 'Child' to return type T
    }
}

      

The idea is to create a simple method that returns a new copy of the child instance with the same values. I don't want to write a constructor for every child class. (it has a lot of parameters in real classes and I like to keep it clean).

The error I am getting: The cannot convert return expression of type 'Child' to return type T

suggested solution is to do it return clone as! T

. But this way I lose the reason for using the generic class.

Any idea how to solve this problem while keeping it generalized and not writing out a constructor in every class?

+3


source to share


1 answer


You need to have a return type Self

and not use a generic placeholder bound to Parent

. With a generic placeholder, you say you clone()

can return an instance of any particular concrete type that inherits from Parent

. However, this is not the case - you only want to return instances of the same type as the receiver, which is expressed Self

.

Then you also need to implement an initializer required

so that it can call all subclasses, allowing you clone()

to call them without having to override them.

struct Something {}
struct SomethingElse {}

class Parent {

    var something: Something

    required init(something: Something) {
        self.something = something
    }

    func clone() -> Self {
        // call the initialiser on the dynamic metatype of the instance,
        // ensuring that we're instantiating a Self instance.
        return type(of: self).init(something: something)
    }
}

      

The implementation Child

should then be as simple as:

class Child : Parent {

    var somethingElse: SomethingElse?

    override func clone() -> Self {
        let clone = super.clone()
        clone.somethingElse = somethingElse
        return clone
    }
}

      



However, unfortunately, the call clone()

to super

returns an instance statically injected as Parent

, not Self

- it's filed as an error .

To get around this, you'll have to do some violent hacking:

override func clone() -> Self {

    let clone = super.clone() as! Child
    clone.somethingElse = somethingElse

    func forceCast<T>(_ value: Child) -> T { return value as! T }
    return forceCast(clone)
}

      

The nested function forceCast(_:)

here is good for addressing the fact that we currently cannot directly use Self

in a method (compare Return instancetype in Swift ). And the force executed in this case will always succeed because it super.clone()

will always return an instance Self

, so this method should have Child

.

+1


source







All Articles