简体   繁体   English

Swift 4中具有不同类型的递归通用结构

[英]Recursive generic structs with different types in Swift 4

struct Lock<Element: Hashable> {

    var element: Element

    init(_ element: Element, _ args:[Lock<Element>]? = nil) {
        self.element = element
    }
}

Is it possible to modify this kind of struct to be able call init() with different Element types in arguments? 是否可以修改这种结构,以便能够在参数中使用不同的Element类型调用init()?

Lock("element", [Lock("100")]) it's ok Lock("element", [Lock("100")])没关系

Lock("element", [Lock(100)]) this causes an error: Cannot convert value of type 'Int' to expected argument type 'Lock<_>' Lock("element", [Lock(100)])这会导致错误: 无法将“Int”类型的值转换为预期的参数类型“Lock <_>”

You have 2 options for doing this. 您有2个选项可以执行此操作。

The simple solution, improving on @Cristik's answer, is to have another initialiser that doesn't expect another generic parameter: 改进@ Cristik的答案的简单解决方案是使用另一个不期望另一个通用参数的初始化器:

struct Lock<Element> {
    var element: Element

    init(_ element: Element) {
        self.element = element
    }
    init<T>(_ element: Element, _ args: [Lock<T>]?) {
        self.init(element)
    }
}

You can then have the code you want above, but you lose the information that Element conforms to Hashable . 然后,您可以获得上面所需的代码,但是您将丢失Element符合Hashable的信息。


Your second option is to create a protocol using an associatedtype . 您的第二个选择是使用associatedtype创建protocol Using a similar trick with having 2 init s, you can do the same except explicitly defining the types: 使用具有2个init的类似技巧,除了明确定义类型之外,您可以执行相同的操作:

protocol Lock {
    associatedtype Element

    init(_ element: Element)
    init<T>(_ element: Element, _ args: [T]?) where T: Lock
}

struct HashableLock<H: Hashable>: Lock {
    typealias Element = H

    var element: Element

    init(_ element: Element) {
        self.element = element
    }
    init<T>(_ element: Element, _ args: [T]?) where T: Lock {
        self.init(element)
    }
}

struct IntLock: Lock {
    typealias Element = Int

    var element: Int

    init(_ element: Int) {
        self.element = element
    }
    init<T>(_ element: Int, _ args: [T]?) where T: Lock {
        self.init(element)
    }
}

Then you can create locks like this: 然后你可以创建这样的锁:

let stringStringLock = HashableLock("element", [HashableLock("100")])
let stringIntLock = HashableLock("element", [IntLock(100)])

The first version is a lot cleaner but it's more limiting. 第一个版本更清洁,但它更具限制性。 It's up to you which one to use, depends on your needs. 这取决于您使用哪一个,取决于您的需求。

This cannot be done. 这是不可能做到的。

The reason is that Element gets assigned to the type used for the element parameter in init . 原因是Element被赋予init用于element参数的类型。 Now that same type has to be used whenever Element appears in the struct but if you want the generic type of Lock to be anything else, you'll need to declare another generic type. 现在,只要Element出现在struct中,就必须使用相同的类型,但是如果你想要Lock的泛型类型,你需要声明另一个泛型类型。

The problem here is that by adding another generic type to Lock (ie Lock<Element, AnotherElement> ) is that now the Lock in your init will need to specify two generic types, and so on. 这里的问题是,通过向Lock添加另一个泛型类型(即Lock<Element, AnotherElement> ),现在initLock将需要指定两个泛型类型,依此类推。

You might get better results by making the initializer generic: 通过使初始化程序通用,您可能会获得更好的结果:

struct Lock<Element: Hashable> {

    var element: Element

    init<T>(_ element: Element, _ args:[Lock<T>]? = nil) {
        self.element = element
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM