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?
source to share
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
.
source to share