Swift Make Method Parameter Mutable

Swift make method parameter mutable?

As stated in other answers, as of Swift 3 placing var before a variable has been deprecated. Though not stated in other answers is the ability to declare an inout parameter. Think: passing in a pointer.

func reduceToZero(_ x: inout Int) {
while (x != 0) {
x = x-1
}
}

var a = 3
reduceToZero(&a)
print(a) // will print '0'

This can be particularly useful in recursion.

Apple's inout declaration guidelines can be found here.

Making immutable function parameter mutable

You could either make that array available to all functions by declaring it as a property (and removing the class modifier of your function) or you could define your function's parameter as an in-out parameter. By adding the inout keyword right before the parameter's type, you can make the parameter mutable by keeping the reference to the passed array.

Nevertheless, I would recommend you to make your array into a property – simply because it's easier and, in my opinion, better maintainable.

Modifying an array passed as an argument to a function in Swift

You need to change the definition of modify to accept an inout parameter. By default, function arguments are immutable, but by using the inout keyword, you can make them mutable. You also need to pass the argument by reference.

func modify( arr: inout [String]) {
arr.removeFirst()
}
var stringArr = ["a","b"]
modify(arr: &stringArr) //stringArr = ["b"] after the function call

Is there any way to make the method return a mutable value?

The callAsFunction simply returns (a copy of the) Person, which is a value type. You cannot then mutate the property of it like that. It is equivalent to the following:

struct Person {
var name: String
}

Person(name: "Foo").name = "Bar"

That returns the same error:

Sample Image

If Person was a reference type, it would have worked, but not for a value type. And even if you took your value type, and first assigned it to a variable before mutating it, you would only be mutating your copy, not the original.

If you want the behavior you want, you would use a @dynamicMemberLookup as suggested by matt (+1) and outlined in SE-0195.


You said:

I can't use dynamicMemberLookup because I don't know what method or property there will be in Person. For example, there may be 100 methods and properties in Person (not only one name property as demonstrated), and it is impossible for me to write 100 subscript methods with dynamicMemberLookup.

You do not need “100 subscript methods.” It is the motivating idea behind @dynamicMemberLookup, namely that the properties will be determined dynamically. E.g., here is Person with two properties, but Group only has the one @dynamicMemberLookup.

struct Person {
var name: String
var city: String
}

@dynamicMemberLookup
struct Group {
var person: Person
subscript(dynamicMember keyPath: WritableKeyPath<Person, String>) -> String {
get { person[keyPath: keyPath] }
set { person[keyPath: keyPath] = newValue }
}
}

var group = Group(person: Person(name: "James", city: "New York"))
group.name = "Wong"
group.city = "Los Angeles"
print(group.person) // Person(name: "Wong", city: "Los Angeles")

If you want to handle different types, make it generic:

struct Person {
var name: String
var city: String
var age: Int
}

@dynamicMemberLookup
struct Group {
var person: Person
subscript<T>(dynamicMember keyPath: WritableKeyPath<Person, T>) -> T {
get { person[keyPath: keyPath] }
set { person[keyPath: keyPath] = newValue }
}
}

And

var group = Group(person: Person(name: "James", city: "New York", age: 41))
group.name = "Wong"
group.city = "Los Angeles"
group.age = 42
print(group.person) // Person(name: "Wong", city: "Los Angeles", age: 42)

Why are function parameters immutable in Swift?

The motivation for this is described here: https://github.com/apple/swift-evolution/blob/master/proposals/0003-remove-var-parameters.md

tl;dr: it avoids confusion with the inout keyword.

Mutate function parameters in Swift

It seems from your example that you need to change the passed arguments inside the function as a side effect, and need the updated values to be available after the function call. If that's the case, you need to use the inout modifier.

Otherwise, if all you need is to modify the parameters inside of the function call, you can explicitly define them as variables on the function definition:

Using in-out parameters

First, change your function declaration to:

func exampleFunction(inout value: String, index: inout Int) -> Bool

Now, an in-out parameter has a value that is passed in to the function, is modified by the function, and then is passed back out of the function, replacing the original value. For this to work, you can't pass a literal into your function, since there would be nowhere to store the modified value afterwards. In fact, it has to be a variable.
You cannot pass a constant or a literal value as the argument, because constants can't be modified. Hence, change your function call to:

var passed = "passedValue"
var index = 2

var b = exampleFunction(&passed, &index)

After the call, both passed and index will contain the new values, as modified by the function.

Also, note the & before each argument when calling the function. It must be there, to indicate the argument can be modified by the function.

Using variable parameters – Removed in Swift 3

In this case, all you need to do is change your function declaration to use variable parameters, as below:

func exampleFunction(var value: String, var index: Int) -> Bool

Changes made to the arguments inside the scope of the function, aren't visible outside the function or stored anywhere after the function call.

How to denote mutable parameters in closures with Swift 2.2?

Closures can mutate inout arguments just fine:

var str1 = "Pine"
var str2 = "Juniper"

let closure = { (s1: inout String, s2: inout String) -> Void in
s1 = "Oak"
s2 = "Chestnut"
}

closure(&str1, &str2)

print(str1, str2)

The problem you are facing is Array.map doesn't have a method signature that includes an inout parameter.

The only way around this that I can think of is to extend Array and add the map method yourself:

extension Array {
func map<T>(_ closure: (inout T) -> Void) -> Array<T> {
var arr = [T]()
for i in 0..<self.count {
var temp : T = self[i] as! T
closure(&temp)
arr.append(temp)
}
return arr
}
}

var hi = "hi", there = "there", world = "world"
var demoStrings = [hi, there, world]
var result = demoStrings.map { (word: inout String) in
word.remove(at: word.startIndex)
}

print(result) // ["i", "here", "orld"]

How to change $0 to be mutable?

You don't need $0 to be mutable. map will use whatever value you return, so you can use the last map like this:

.map {$0 == "123" ? "replace" : $0}

When that map closure is run, whenever $0 matches "123", it will return replace, otherwise it will return the current value.

Mutable parameters on swift closure using Objective-c typedef

Generally you can set stop using memory like so:

if !continueFlag { stop.memory = true }

Or, you can do:

stop.memory = ObjCBool(!continueFlag)


Related Topics



Leave a reply



Submit