I'm trying to create a property wrapper with a few a few different initializers depending on generics, but it seems that generic constraints break delayed initialization.
@propertyWrapper
struct PW<A, B> {
var wrappedValue: A
init(wrappedValue: A) where A == B {
self.wrappedValue = wrappedValue
}
}
struct X {
@PW var x: Int // π Generic parameter 'B' could not be inferred
}
If I remove the B generic from PW entirely, or if I eagerly assign a value:
struct X {
@PW var x = 1 // PW<Int, Int>
}
The inference works just fine.
Property wrapper initialization is certainly nuanced, so I might be missing what part of the feature prevents this from working, but should it work?
Random old thread revival, I just stumbled onto this while searching for something else.
It makes sense to me that this happens, because without supplying a value youβre free to use any init of the property wrapper itself later, like:
struct X {
@PW var x: Int
init() {
_x = .init(someOtherInitParameter: ...)
}
}
And that init might have other, or no, constraints among the type parameters. With the inline value the compiler synthesizes the init(wrappedValue:) which is enough to infer the constraint. If you "init" the wrapper with a wrapped value out of line it will also synthesize the init(wrappedValue:) but it will do so where you assign the value. This:
struct X {
@PW var x: Int
init() {
x = 1
}
}
becomes:
struct X {
@PW var x: Int
init() {
_x = .init(wrappedValue: 1)
}
}
But the compiler can't use the out of line initializer to infer type parameters so that doesn't compile either.
What you could do instead is use a typealias for the constrained version, like this:
@propertyWrapper
struct GenericPW<A, B> {
var wrappedValue: A
init(wrappedValue: A) where A == B {
self.wrappedValue = wrappedValue
}
}
typealias PW<A> = GenericPW<A, A>
struct X {
@PW var x: Int
}
What would be neat is if you could conditionally make a generic type a property wrapper (i.e. something like extension PW: @propertyWrapper where A == B, and then have the compiler infer the constraint from the fact it's used as a wrapper.
My guess here is this is one reason why SwiftData @Query "property wrappers" actually look like they are macros. It's possible there's no clean way to do this directly with a conventional property wrapper.