【问题标题】:Generics: is there a protocol for InitializableWithString?泛型:是否有 InitializableWithString 的协议?
【发布时间】:2016-08-13 05:56:33
【问题描述】:

我正在尝试构建一个可以从字符串初始化的通用结构。

小例子:

struct Example<T> {
    var data: T

    init(fromString string: String) {
        data = T(string)
    }
}

这自然会失败,因为我们必须确保 T 可以是来自 String 的 init()。

我找不到这个协议,所以我尝试创建它:

protocol InitializableWithString { init?(_: String) }

struct Example<T: InitializableWithString> { ... }

但尝试使用 Int 会给出:

let testVar = Example<Int>(fromString: "12")

错误:类型“Int”不符合协议“InitializableWithString”

但是 Ints 确实有一个带有 String 参数的可失败初始化器。

> let intFromString = Int("12")
intFromString: Int? = 12

有没有办法完成我在这里尝试做的事情?

【问题讨论】:

  • 可能不是你要找的,但是有一个可以用字符串 literals 初始化的类型的协议:StringLiteralConvertible(Swift 3 中的ExpressibleByStringLiteral) .

标签: swift generics


【解决方案1】:

AFAIK 表示可以使用 String 创建的类型的协议不存在。

您定义了自己的,这很好,但有一些错误。

1。命名参数

protocol InitializableWithString {
    init?(string:String)
}

2。使Int符合InitializableWithString

Int 结构体有一个初始化器,用于从String 创建一个Int,它在我们编写时使用

Int("123")

此初始化程序具有以下签名

public init?(_ text: String, radix: Int = default)

此解决方案的缺失部分是扩展Int 以使其符合InitializableWithString,如下所示

extension Int: InitializableWithString {
    init?(string: String) {
        self.init(string)
    }
}

3。使Exampleinit 也可以失败

由于InitializableWithString 的初始化程序如果失败,您将无法构建T 类型。在这种情况下,Exampleinit` 应该做什么?最简单的解决办法就是让它失败

struct Example<T where T: InitializableWithString> {
    var data: T

    init?(string: String) {
        guard let data = T(string: string) else { return nil }
        self.data = data
    }
}

结论

现在你可以写了

let example = Example<Int>(string: "123")
if let example = example {
    print(example.data)
}

更多

现在您可以让更多类型符合InitializableWithString,然后使用它们来构建新的Example(s) 值。

【讨论】:

  • 非常感谢您的详细解释。
【解决方案2】:

问题在于您没有明确地使 Int 符合您的 InitializableWithString 协议,因此即使它可能隐式地符合,Swift 的类型系统也不会识别这一事实。

您可以通过扩展明确地符合它:

extension Int : InitializableWithString {}

但是,我们仍然得到错误:

类型“Int”不符合协议“InitializableWithString”

现在的问题是,虽然Int 可以用String 初始化,但它是这样做的through this initialiser,它有两个参数——radix: 参数恰好有默认值:

/// Construct from an ASCII representation in the given `radix`.
///
/// If `text` does not match the regular expression
/// "[+-]?[0-9a-zA-Z]+", or the value it denotes in the given `radix`
/// is not representable, the result is `nil`.
public init?(_ text: String, radix: Int = default)

因此 Swift 无法识别 Int 可以满足 init?(_: String) 的要求。因此,解决方案是实现一个满足此要求的初始化程序,并使用默认的radix 值将调用转发到init?(_:radix:) 初始化程序:

extension Int : InitializableWithString {
    init?(_ str: String) {
        self.init(_:radix:)(str) // the (_:radix:) is used in order to disambiguate
    }
}

或者如果您还希望能够处理非十进制数字表示,则将您的协议更改为需要 radix: 参数:

protocol InitializableWithString {
    init?(_: String, radix:Int)
}

有了这个,你就不需要实现额外的初始化器了,Int 直接满足了这个要求。

【讨论】:

  • 感谢您的解释,非常感谢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-01-12
  • 1970-01-01
  • 2016-11-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多