我是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("#")
但是如果我把一些具有变异函数的结构并调用该函数 - 编译器显然告诉我:
错误:不能在不可变值上使用变异成员:函数调用返回不可变值test.get()。append(“#”)
Swift的Optional如何通过引用返回struct来展开它们?
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 = ...
和test[] = ...
),吸气剂不会被召唤;只有setter,它给了他们与Optional
的postfix !
略有不同的语义,即使在赋值时,它也会在nil
上被选中(即someOptional! = ...
)。
作为替代方案,您还可以定义一个方法,该方法使用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("#") }
您可以使用非变异操作,并将结果重新分配给变量:
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)