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

Swift 为什么我的结构在方法链中变得不可变?,swift,struct,value-type,Swift,Struct,Value Type,在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(&

在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"]
一般来说,我不太熟悉变异函数、inout参数或Swift,但上面的代码对我来说应该很有用<代码>点击在未包含在方法链中时按预期工作。当我将其作为方法链的一部分包含时,如上例所示,Swift编译器会抱怨:

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

有人能解释一下为什么这不起作用吗?谁能提供一个有效的解决方案并解释为什么该解决方案有效

编辑: 另一个示例用法是:

let user = User(fromId: someId).tap {
   $0.firstName = someFirstName
   $0.lastName = someLastName
}

tap
是Ruby提供的一种便捷功能。我主要感兴趣的是理解为什么函数中的类型工作不正常。

返回self返回原始数组的副本,而不是原始数组本身。在将此副本存储为
var
之前,无法对其进行变异。因此,这将起作用:

var b = a.tap {
  $0.append("5")
}
b.tap {
  $0.append("7")
}
但必须先将
b
存储为
var
。当然,您首先不会创建
b
,您只需重复使用
a
,正如您已经指出的那样

因此,问题是您可以完成
tap
一次,但不能链接
tap
s。这是因为
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")})
因为每次调用
tap(
都会返回一个由给定块修改的原始数据的副本,所以您可以在不可变类型上调用它。这意味着您可以链接


唯一的缺点是新的
a=
在开头。

有趣的问题…我很想听到完整的答案,但这里有一些初步的想法,虽然它似乎工作正常,但可能会对一些代码产生一些意想不到的后果。当您将&self传递到块时,因为它是一个变异函数,所以根据块改变self的值。但是,返回self也会生成数组的单独副本。因此,如果设置b=a.tap{…},将对“a”进行变异,您将在“b”中收到它的一个独立副本。看起来您不会同时进行这两种操作,而只是在适当的位置对“a”进行变异,对吗?根据本文,我认为像数组这样的结构不可能实现所需的行为。