Partial Application of 'Mutating' Method Is Not Allowed

Partial application of 'mutating' method is not allowed

The key to a value type is that assignment creates a copy. This contract also affects how people can reason about their code. For example if you're passed an Int into a method, you can be confident that the value won't change out from under you, even if the original int passed in gets sent off to another thread and has calculations done elsewhere.

Same is true for structs. This is why in swift when defining methods that may alter 'self', if it's a value type you have to define it as 'mutating'. What this says is that it will simultaneously reassign to your variable with the new value. So for example When you call your add method with '3', you can think of it performing something similar to:

var myStruct = MyStruct()
var tmp = myStruct
tmp.count = tmp.count + 3
myStruct = tmp

Now the reason that you are hitting an error is because partially applying a mutating function would break that contract. If you are able to save a closure like let myStructAdd = myStruct.add, then at some later point you could call myStructAdd(3) and it would try to change myStruct. This would give reference semantics to a value type since now you have the power to alter myStruct at a later point, even from a different method.

In short, swift is giving you a convenience by providing 'mutating' methods for code readability, with the caveat that it has to happen all at once so as not to break the value type contract.

Using a class inside a struct is giving an error: partial application of 'mutating' method is not allowed

SwiftUI Views should not have mutating properties/functions. Instead, they should use property wrappers like @State and @StateObject for state.

Besides the mutating function, you're fighting the principals of SwiftUI a bit. For example, you should never try to keep a reference to a View and call a function on it. Views are transient in SwiftUI and should not be expected to exist again if you need to call back to them. Also, SwiftUI tends to go hand-in-hand with Combine, which would be a good fit for implementing in your Timer code.

This might be a reasonable refactor:

import SwiftUI
import Combine

// Class with the timer logic
class TimerLogic : ObservableObject {
@Published var timerEvent : Timer.TimerPublisher.Output?
private var cancellable: AnyCancellable?

func startTimer() {
cancellable = Timer.publish(every: 3.0, on: RunLoop.main, in: .default)
.autoconnect()
.sink { event in
self.timerEvent = event
}
}

func stopTimer() {
cancellable?.cancel()
}
}

struct ContentView: View {

//Timer to send information to phone
@StateObject var timerLogic = TimerLogic()

var body: some View {
Button(action: timerLogic.startTimer) {
Image(systemName: "location")
}
.onChange(of: timerLogic.timerEvent) { _ in
timerTicked()
}
}

// Function to run with each timer tick, this can be any action
func timerTicked() {
print("Timer ticked...")
//...
}
}

What alternative when I get: Partial application of 'mutating' method is not allowed

One way is to accept an inout Test in technique. Also you should make apply mutating, because it undoubtedly changes the state of Test.

mutating func apply(technique: (inout Test, Int) -> ()) {
print("Before:\(value)")
technique(&self, 2) // this is "kind of" analogous to "self.technique(2)"
print("After:\(value)")
}

mutating func main() {
apply(technique: {
$0.increment(amount: $1)
})
}

Swift mutating Function as first class value

The reason why you get a warning (and soon to be an error in Swift 5 mode) on:

extension Array where Element == Int { 
mutating func swapFirstTwo2() {
if count >= 2 {
swapAt(0, 1)
}
}
}

typealias Swapper2 = (inout [Int]) -> () -> ()
let swapThemAgain: Swapper2 = Array.swapFirstTwo2

is due to the fact that inout arguments are only valid for the duration of the call they're passed to, and therefore cannot be partially applied.

So if you were to call the returned (inout [Int]) -> () -> () with &array2, you would get back a () -> () which now has an invalid reference to array2. Attempting to call that function will yield undefined behaviour, as you're trying to mutate an inout argument outside of the window where it's valid.

This problem will be fixed if/when unapplied instance methods get flat signatures, as Array.swapFirstTwo2 will instead evaluate to a (inout [Int]) -> ().

But in the mean time, you can workaround the issue by using a closure instead:

typealias Swapper2 = (inout [Int]) -> ()
let swapThemAgain: Swapper2 = { $0.swapFirstTwo2() }

var array2 = [1,2,3]
print(array2) // [1, 2, 3]
array2.swapFirstTwo2()
print(array2) // [2, 1, 3]

Swift 2: mutating method on struct not working from inside closure

This seems to work ok:

dispatch_after(time, highQ, { self.callChangeTimeStamp() })

Assign method (function of an object) to a variable or how to pass a parameter to unit tests

It turns out that for each instance method a type has, there's a
corresponding static method that lets you retrieve that instance
method as a closure, by passing an instance as an argument.

For example: [3, 2, 1].sort() == Array.sort([3, 2, 1])

Interesting fact, but pretty useless in our case, because

Partial application of 'mutating' method is not allowed.

So as long as I can't pass a mutating method as a parameter, I will pass an enumeration object:

extension Array where Element == Int {

mutating func sort(_ algorithm: SortAlgorithm) {
switch algorithm {
case .bubble: bubbleSort()
case .selection: selectionSort()
}
}
}

enum SortAlgorithm {
case bubble
case selection
}

As for second part of the question I think the best answer is inheritance:

// MARK: - Basic UnitTest Class

class SortingTest: XCTestCase {
static var algorithm: SortAlgorithm!

override static func tearDown() {
algorithm = nil
super.tearDown()
}

func testSorting1() {
var a = [9, 2, 5, 3, 8, 1, 4, 7, 6]
a.sort(Self.algorithm)
XCTAssertEqual(a, [1, 2, 3, 4, 5, 6, 7, 8, 9])
}

func testSorting2() {
var a = [4, 3, 2, 1]
a.sort(Self.algorithm)
XCTAssertEqual(a, [1, 2, 3, 4])
}
......

// MARK: - Inherited UnitTest Classes

class BubbleTest: SortingTest {

override static func setUp() {
super.setUp()
algorithm = .bubble
}
}

class SelectionTest: SortingTest {

override static func setUp() {
super.setUp()
algorithm = .selection
}
}

Everything is ready for nicely calling tests for appropriate sorting algorithm:

extension enum SortAlgorithm {

func runTests() {
switch self {
case .bubble: BubbleTest.defaultTestSuite.run()
case .selection: SelectionTest.defaultTestSuite.run()
}
}
}

SortAlgorithm.bubble.runTests()

P.S. Thank @DávidPásztor and @matt for hints :)

didSet in Swift has a weird knock-on effect on mutating func

in the newest accepted proposal 0042-flatten-method-type, self is no more passed as curried function, so this problem is solved in the Swift 3



Related Topics



Leave a reply



Submit