为什么我的结构在方法链中变得不可变?

G-P*_*G-P 5 struct value-type swift

在 Swift 中,我试图实现一种类似于 Ruby 中存在的方法“tap”的方法。

我想出了以下示例代码:

private protocol Tap {
    mutating func tap(_ block: (inout Self) -> Void) -> Self
}

private extension Tap {
    mutating func tap(_ block: (inout Self) -> Void) -> Self {
        block(&self)
        return self
    }
}

extension Array: Tap {}

var a = Array(repeating: "Hello", count: 5)

a.tap {
    $0.append("5")
}.tap {
    $0.append("7")
}

print(a)  // (Expected) => ["Hello", "Hello", "Hello", "Hello", "Hello", "5", "7"]
Run Code Online (Sandbox Code Playgroud)

我对变异函数、inout 参数或 Swift 不太熟悉,但上面的代码看起来应该对我有用。tap当它没有包含在方法链中时,它会按预期工作。当我将它作为方法链的一部分包含时(如上面的示例所示),Swift 编译器会抱怨:

无法对不可变值使用变异成员:函数调用返回不可变值

谁能向我解释为什么这不起作用?任何人都可以提供一个可行的解决方案并解释为什么该解决方案有效吗?

编辑:

另一个示例用法是:

let user = User(fromId: someId).tap {
   $0.firstName = someFirstName
   $0.lastName = someLastName
}
Run Code Online (Sandbox Code Playgroud)

tap是来自 Ruby 的一个便利的东西。我主要感兴趣的是理解为什么我的函数中的类型不能正常工作。

小智 1

返回return self原始数组的副本,而不是原始数组本身。在将此副本存储为 之前var,它无法被更改。所以,这会起作用:

var b = a.tap {
  $0.append("5")
}
b.tap {
  $0.append("7")
}
Run Code Online (Sandbox Code Playgroud)

但并非没有b首先存储var。当然,您不会b首先创建 a ,您只会a像您已经指出的那样重复使用。

所以,问题是你可以完成tap一次,但不能连锁tap。这是因为 的返回self是隐式不可变的,并且您不能对不可变值调用可变函数。更改tap为非变异函数可以得到你想要的:

private extension Tap {
    func tap(_ block: (inout Self) -> Void) -> Self {
        let copy = self
        block(&copy)
        return copy
    }
}

var a = Array(repeating: "Hello", count: 5)

a = a.tap({$0.append("5")}).tap({$0.append("7")})
Run Code Online (Sandbox Code Playgroud)

由于每次调用tap(都会返回给定块修改的原始副本的副本,因此您可以在不可变类型上调用它。这意味着你可以连锁。

唯一的缺点是a =一开始就是新的。