Swift enum通过引用展开

时间:2017-11-30 15:30:08

标签: swift enums optional

我是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?

2 个答案:

答案 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.forceUnwrappedtest[]可以视为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)

拥有mapflatMap等功能可能也很有用:

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)