Cannot assign generic closure to variable with arguments of wider type

Hi All.

I'm having trouble assigning to a closure variable with type erased arguments. It seems to me that something like this should work:

class TypeErasedKeyPaths {
    var root:AnyObject?
    var keypath:AnyKeyPath?
    var action:((AnyObject,AnyKeyPath)->Void)?
    
    func takeit<T:AnyObject>(r:T,
                             kp:PartialKeyPath<T>,
                             a:@escaping (T,PartialKeyPath<T>)->Void)
    {
        self.root = r        // works
        self.keypath = kp    // works
        self.action = a      // Cannot assign value of type '(T, PartialKeyPath<T>) -> Void' to type '((AnyObject, AnyKeyPath) -> Void)?'
    }
}

Is this the same bug as SR-6740/SR-5667?
Is there any trick I can use to make this work?

Thanks!

I think the compiler is right. Consider a simpler example:

protocol Animal {
    var weapon: String { get } // All animals have a weapon of some sort.
}
struct Slug: Animal {
    var weapon: String { return "poison" }
}
struct Cat: Animal {
    var weapon: String { return "claws" }
    var noise: String { return "meow" } // Not all animals make a noise.
}
func weapon(a: Animal) -> String { return a.weapon }
func noise(c: Cat) -> String { return c.noise }
struct ApplyLater {
    let a: Animal
    let f: (Animal) -> String
    var apply: String { return f(a) }
}
let slugWeapon = ApplyLater(a: Slug(), f: weapon(a:)) // OK
let catWeapon = ApplyLater(a: Cat(), f: weapon(a:)) // OK
// let slugNoise = ApplyLater(a: Slug(), f: noise(c:)) // ERROR: Obvious that this doesn't work because a slug doesn't make a noise.
// let catNoise = ApplyLater(a: Cat(), f: noise(c:)) // ERROR: Not so obvious that this doesn't work because a cat does make a noise. However it is typed as an `Animal` and an animal doesn't necessarily make a noise.

You are doing the equivalent of the catNoise example and it fails because not all animals can make a noise.

Thanks!

The same thing occurred to me while I was walking the dog...

As it turns out, this is exactly the same question I asked on the forum last time: Declaring a property with closure argument typed to Self

I guess I'm a slow learner (future employers: please ignore this)

I guess there are two rules of thumb that I have to throw away:

  • generics can (successfully) replace id in every situation
  • generics means never having to say you're "sorry - need to cast"

For future reference, you declare variables with wider type than the things you assign to them (e.g. put a dog into an Animal type), but when you have closures, the arguments you declare must be equal to or narrower than what you assign to them. (e.g. declared to accept Dog, so you can assign a function that takes an Animal).

This has worked well for me: https://www.stephanboyer.com/post/132/what-are-covariance-and-contravariance

2 Likes