On Thursday, December 10, 2015, Kevin Ballard via swift-evolution < swift-evolution@swift.org> wrote:
A couple of miscellaneous points:
* This problem only occurs when the class inherits a default
implementation of the method from a protocol extension. If the class
declares the method itself, then it's no longer a mutating method and
everything works fine.
* The problem exists because mutating functions are allowed to assign to
self, but methods on class types cannot assign to self, they can only
mutate the properties of self. This is why we cannot simply allow the call
to the inherited mutating method, as that method may reassign self.
* Classes can still call the method, they just have to say something like
var this = self
this.callMutatingMethod()
Yeah it's a little awkward, but it's not really all that bad for an edge
case like this.
Another potential workaround requires more work on the protocol side but
allows implementations to not care about the difference, which is to
provide a non-mutating variant in an extension restricted by Self:
AnyObject:
protocol P {
var count: Int { get set }
mutating func foo() -> String
}
extension P {
mutating func foo() -> String {
return _mutatingFoo(&self)
}
}
extension P where Self: AnyObject {
func foo() -> String {
var this = self
return _mutatingFoo(&this)
}
}
private func _mutatingFoo<T: P>(inout value: T) -> String {
value.count += 1
return "foo"
}
Ultimately, I think there's some value in this proposal, but I worry about
adding a new keyword to handle an edge case like this when there's
workarounds available. Assuming we do add this feature, I'd suggest using
something like `mutating(ish)` instead of inventing a brand new keyword
(there's precedent in `private(set)`).
-Kevin Ballard
On Thu, Dec 10, 2015, at 02:35 PM, Josh Avant via swift-evolution wrote:
Currently, when a reference-type adopts a protocol with a function
declared as `mutating`, the reference-type's implementation cannot call
that function internally. This is because the compiler enforces an
immutable `self` pointer value, and the `mutating` qualifier implies that
the function implementation may mutate that `self` pointer value.
However, there seems to be a number of fairly reasonable situations where
a reference-type implementation of these `mutating` functions may only want
to mutate properties owned by `self`, but not the actual `self` pointer
value.
Consider this toy example:
import Foundation
protocol RandomDataTransformable {
typealias TransformableType
var data: [TransformableType] { get set }
mutating func addRandomData()
}
extension RandomDataTransformable where TransformableType == Int {
mutating func addRandomData() {
let random = Int(arc4random_uniform(6) + 1)
data.append(random)
}
}
/////
// VALID
struct NumberSource_Struct : RandomDataTransformable {
typealias TransformableType = Int
var data: [Int] = []
mutating func addData() {
addRandomData()
}
}
// VALID
class NumberSource_ClassDeclaration: NSObject, RandomDataTransformable {
typealias TransformableType = Int
var data: [Int] = []
}
var numberSource = NumberSource_ClassDeclaration()
numberSource.addRandomData()
// INVALID
class NumberSource_ClassImplementation: NSObject, RandomDataTransformable {
typealias TransformableType = Int
var data: [Int] = []
func addData() {
self.addRandomData() // Compiler Error: Cannot use mutating member
on immutable value: 'self' is immutable
}
}
Even despite the fact that the default implementation for `addRandomData`
does not mutate the `self` pointer value, reference-type implementations
are unable to call that function internally, since it is marked as
`mutating`.
Perhaps confusingly, `addRandomData` may be called by externally, by
objects which own instances of the reference-type (even though, again, it
may not called internally by the implementation, itself).
Currently, the only solution to allow reference-type implementations to
call the sample `addRandomData` implementation internally is to qualify the
whole `RandomDataTransformable` protocol as `class`. The downside here is
that this takes an otherwise perfectly reference- and struct-compatible
protocol + extension implementation and restricts it to only apply to
classes, decreasing overall code reusability.
My proposal would be to introduce an intermediate mutation qualifier that
applies when protocols are adopted by reference-types. The qualifier would
specify that the `self` pointer value itself may not be mutated, but
`self`'s properties may be, as appropriate.
Thoughts, feedback on this?
*_______________________________________________*
swift-evolution mailing list
swift-evolution@swift.org
<javascript:_e(%7B%7D,'cvml','swift-evolution@swift.org');>
https://lists.swift.org/mailman/listinfo/swift-evolution