Swift: How to hold any possible instance of a gene

2020-07-17 16:14发布

问题:

The distillation of what I am trying to do is this:

public struct HolderOfWrappers
{
    let anyWrappedItem: MyResource<Any>
}

public struct MyResource<A>
{
    let wrappedItem : A
    let convert: String -> A
}

func holdResource<A>( resource: MyResource<A> ) -> HolderOfWrappers
{
    // Error on this line, A is not Any...
    let wrapHolder : HolderOfWrappers = HolderOfWrappers( resource )
    return wrapHolder
}

As it stands, this code produces the compiler error in the last holdResource method where I'm trying to build a HolderOfWrappers:

Cannot convert the expression's type 'MyResource<A>' to type '(anyWrappedItem: MyResource<Any>)'

Which is understandable as the code indicates HolderOfWrappers can only hold a MyResource built for Any type, not any possible type. What I'm really after with the HolderOfWrappers is something like this:

public struct HolderOfWrappers
{
    let anyWrappedItem: MyResource<>
}

or even MyResource<*> - I am trying to say with this code that I'd like a variable that can hold any type of MyResource. If I try to use either syntax though, I get a compiler error that it expects a type.

I could just have anyWrappedItem by of type Any, but then you lose the type information for future use. I also do not want HolderOfWrappers to be generic (because then I'd just have the same problem later).

It's almost like I am trying to treat the generic type as a protocol for the anyWrappedItem storage variable, which will not work for other reasons...

回答1:

I think you can do what you want by putting a generic parameter in your HolderOfWrappers init method. Basically, the init method just generates a new MyResource using the resource that you provide, like this:

public struct HolderOfWrappers {

    let anyWrappedItem: MyResource<Any>

    public init<A>(resource: MyResource<A>) {
        self.anyWrappedItem = MyResource(wrappedItem: resource.wrappedItem, convert: resource.convert)
    }
}

I think that will do what you want. I don't know if it will be slower since you are initializing an entirely new MyResource instead of just copying one.

At any rate, it makes it so that HolderOfWrappers itself is not generic and will fill anyWrappedItem with a MyResource<Any> that holds the same values as the resource that you pass in.



回答2:

How about this

protocol Wrapper {}

public struct HolderOfWrappers {
    let anyWrappedItem: MyResource<Wrapper>
}

public struct MyResource<A> {
    let wrappedItem : A
    let convert: String -> A
}

func holdResource( resource: MyResource<Wrapper>) -> HolderOfWrappers {
    // Error on this line, A is not Any...
    let wrapHolder : HolderOfWrappers = HolderOfWrappers(anyWrappedItem: resource)
    return wrapHolder
}

of course the downside is you'd have to do extension A: Wrapper { } for any type A that you pass into MyResource.