简体   繁体   English

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

[英]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? 任何人都可以提供有效的解决方案并解释该解决方案的原因

Edit: 编辑:

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(&copy)
        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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM