[英]Why does my struct become immutable in a method chain?
In Swift I am trying to implement a method "tap" similar to the method which exists in Ruby. 在Swift中,我试图实现一个方法“tap”,类似于Ruby中存在的方法。
I've come up with the following example code: 我想出了以下示例代码:
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"]
I'm not super familiar with mutating functions, inout parameters, or Swift in general, but the code above looks like it should work to me. 我不太熟悉变异函数,inout参数或Swift,但上面的代码看起来应该对我有用。
tap
works as expected when it's not being included in a method chain. 当它没有包含在方法链中时,
tap
按预期工作。 When I include it as part of a method chain, like in the above example, the Swift compiler complains: 当我将它作为方法链的一部分包含在内时,就像上面的例子一样,Swift编译器抱怨:
Cannot use mutating member on immutable value: function call returns immutable value
不能在不可变值上使用变异成员:函数调用返回不可变值
Can anyone explain to me why this doesn't work? 任何人都可以向我解释为什么这不起作用? Can anyone provide a working solution and explain why that solution works?
任何人都可以提供有效的解决方案并解释该解决方案的原因
Another example usage would be: 另一个示例用法是:
let user = User(fromId: someId).tap {
$0.firstName = someFirstName
$0.lastName = someLastName
}
tap
is a convenience thing that comes from Ruby. tap
是Ruby的便利之物。 I'm mainly interested in understanding why the types in my function aren't working out right. 我主要是想了解为什么我的函数中的类型不正确。
The return self
returns a copy of the original array, not the original array itself. return self
返回原始数组的副本,而不是原始数组本身。 Until this copy is stored as a var
, it cannot be mutated. 在将此副本存储为
var
,不能对其进行变异。 So, this would work: 所以,这将工作:
var b = a.tap {
$0.append("5")
}
b.tap {
$0.append("7")
}
But not without storing b
as a var
first. 但不是先将
b
存储为var
。 Of course, you wouldn't create a b
in the first place, you would just use a
repeatedly as you already pointed out. 当然,你不会首先创建一个
b
,你只a
反复使用,就像你已经指出的那样。
So, the issue is that you can accomplish tap
once, but cannot chain tap
s. 所以,问题是你可以完成一次
tap
,但不能连接tap
。 This is because the return of self
is implicitly immutable, and you cannot call a mutating function on an immutable value. 这是因为
self
的返回是隐式不可变的,并且您不能在不可变值上调用变异函数。 Changing tap
to a non-mutating function could get you what you want: 将
tap
更改为非变异函数可以获得您想要的内容:
private extension Tap {
func tap(_ block: (inout Self) -> Void) -> Self {
let copy = self
block(©)
return copy
}
}
var a = Array(repeating: "Hello", count: 5)
a = a.tap({$0.append("5")}).tap({$0.append("7")})
Because each invocation of tap(
returns a copy of the original modified by the given block, you can call it on immutable types. That means that you can chain. 因为每次调用
tap(
返回由给定块修改的原始副本,你可以在不可变类型上调用它。这意味着你可以链接。
The only downside is that new a =
in the beginning. 唯一的缺点是新的
a =
开头。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.