Swift enum通过引用展开

问题描述 投票:1回答:2

我是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 enums optional
2个回答
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 = ...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("#") }

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)
© www.soinside.com 2019 - 2024. All rights reserved.