我是Swift的新手并决定编写自己的Optional枚举。
enum MyOptional<Type> {
case none
case some(Type)
func get() -> Type {
switch self {
case .some(let x):
return x
case .none:
fatalError()
}
}
}
var test: MyOptional<String> = MyOptional.some("testString")
test.get().append("#")
但是如果我把一些具有变异函数的结构并调用该函数 - 编译器显然告诉我:
错误:不能在不可变值上使用mutating成员:函数调用 返回不可变值test.get()。append(&#34;#&#34;)
Swift的Optional如何通过引用解包struct?
答案 0 :(得分:4)
Swift编译器对Optional
有相当多的内置支持;包括后缀运算符!
和?
,它们可以生成l值(位于内存中已知位置的值;因此,如果表达式是可变的,则允许该内存的突变)。
不幸的是,我不相信它可以实现你自己的l值返回运算符(或一般的函数),虽然允许你定义getter和setter(比如计算属性和下标)的构造可以被视为l - 当他们有二传手时的价值:
enum MyOptional<Type> {
case none, some(Type)
var forceUnwrapped: Type {
get {
switch self {
case .some(let x):
return x
case .none:
fatalError()
}
}
set {
self = .some(newValue)
}
}
// just for demonstration; don't actually implement this as a subscript!
subscript() -> Type {
get {
switch self {
case .some(let x):
return x
case .none:
fatalError()
}
}
set {
self = .some(newValue)
}
}
}
var test = MyOptional.some("testString")
test.forceUnwrapped.append("#")
test[].append("#")
此处,test.forceUnwrapped
和test[]
可以视为l值。通过它们进行变异时,编译器将通过调用getter创建一个临时变量,将此临时变异,然后使用变异值调用setter。
虽然在两种情况下值得注意的是,当与作业一起使用时(即test.forceUnwrapped = ...
&amp; test[] = ...
),不会调用吸气剂;只有setter,它赋予它们与Optional
的postfix !
稍微不同的语义,即使在赋值时,它也会在可选的nil
上崩溃(即someOptional! = ...
)。< / p>
作为替代方案,您还可以定义一个方法,该方法使用inout
参数进行闭包,允许调用者改变强制解包的值:
enum MyOptional<Type> {
case none
case some(Type)
mutating func forceMutate<R>(_ body: (inout Type) throws -> R) rethrows -> R {
switch self {
case .some(var x):
defer {
self = .some(x)
}
return try body(&x)
case .none:
fatalError()
}
}
}
var test = MyOptional.some("testString")
test.forceMutate { $0.append("#") }
答案 1 :(得分:1)
您可以使用非变异操作,并将结果重新分配回变量:
enum MyOptional<Type> {
case none
case some(Type)
func forceUnwrap() -> Type {
switch self {
case .some(let x):
return x
case .none:
fatalError()
}
}
static func ?? (lhs: MyOptional, rhs: @autoclosure () -> Void) {
}
}
var test: MyOptional<String> = .some("testString")
print(test)
test = .some(test.forceUnwrap() + "#")
print(test)
拥有map
和flatMap
等功能可能也很有用:
extension MyOptional {
func map(_ transform: (Wrapped) -> Wrapped) -> MyOptional<Wrapped> {
switch self {
case .some(let x):
return .some(transform(x))
case .none:
return .none
}
}
mutating func mapInPlace(_ transform: (Wrapped) -> Wrapped) {
self = self.map(transform)
}
}
test = test.map{ $0 + "#" }
print(test)
test.mapInPlace{ $0 + "#" }
print(test)