174

How can I deal with this error without creating additional variable?

func reduceToZero(x:Int) -> Int {
    while (x != 0) {
        x = x-1            // ERROR: cannot assign to 'let' value 'x'
    }
    return x
}

I don't want to create additional variable just to store the value of x. Is it even possible to do what I want?

Cristik
  • 30,989
  • 25
  • 91
  • 127
Gabriel
  • 1,877
  • 2
  • 12
  • 8

7 Answers7

253

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.

Cem Schemel
  • 442
  • 3
  • 13
esreli
  • 4,993
  • 2
  • 26
  • 40
  • 2
    Thank you very much!!!! I am stuck here on a recursion question. You saved my life. – JW.ZG Jul 19 '16 at 21:49
  • 3
    Should use this with caution as this modifies variables outside function scope. Ideally you want to explicitly return the value you changed inside the function. – Chris Gunawardena Aug 14 '16 at 08:50
  • 2
    `inout` keyword should be placed between parameter name and parameter type like this: `func reduceToZero(x: inout Int) ` in current Swift 3 version. – Agustí Sánchez Sep 24 '16 at 08:59
  • Except this doesn't seem to work for closures since closures evidently only capture inout parameters by value (at least that is the error message Xcode gives me). I use @GeRyCh solution in this case. – wcochran Apr 14 '17 at 16:03
  • Thank you. This worked for now, but its like using pointers in C. Would this survive another Swift version? – Krishna Vedula Jul 26 '17 at 12:44
  • 1
    This is not a suitable substitute for removal of the `var` keyword in parameter lists. You should shadow the original (constant) argument with a variable: `var x = x` should be the first line of the function. – BallpointBen Mar 27 '18 at 01:30
  • I wouldn't make a comparison to a pointer for the simple fact that you're not modifying an address that must be dereferenced (in C++ parlance). inout rather makes the behavior similar to a language like Java, which is 'pass by reference' and fully mutable. The Swift designers decided to make method arguments immutable as a guard, so bypassing this behavior has to be given a bit more thought (which you could argue has some wisdom). – SilentDirge Jun 25 '18 at 04:18
  • This changes that Int outside of the function scope though which is bad and may cause unexpected behaviors. – coolcool1994 May 19 '21 at 02:24
  • Downvoting for the comment than `inout` is dangerously not the same as having a mutable local copy. – orion elenzil Sep 29 '22 at 17:48
54

'var' parameters are deprecated and will be removed in Swift 3. So assigning to a new parameter seems like the best way now:

func reduceToZero(x:Int) -> Int {
    var x = x
    while (x != 0) {
        x = x-1            
    }
    return x
}

as mentioned here: 'var' parameters are deprecated and will be removed in Swift 3

Community
  • 1
  • 1
GeRyCh
  • 1,580
  • 3
  • 13
  • 23
  • 1
    In this case, does it actually copy the `x` in the the new `var x`? Or is Swift doing something more efficient than that? – Genki Feb 10 '17 at 20:58
  • 3
    This works and is what I do, but it seems very awkward. – wcochran Apr 14 '17 at 15:40
  • 2
    @Gomfucius Not a word about this in the Swift 3.1 guide. In this case (`x` fits in register) there is virtually no cost. If `x` is array, struct, or object that is mutated, then a copy almost certainly needs to be performed (unless the optimizer can analyze it inline and alias it). – wcochran Apr 14 '17 at 16:00
  • 2
    @wcochran This is a neat trick, but really there's nothing special going on. It's simply eclipsing an input parameter with a local var copy. In the OP's situation it is a better replacement for `var` args than using `inout` which may have unintended side-effects, esp. if the var was a pointer. – Echelon Jun 23 '17 at 17:05
  • So should we use this method or the inout method? – anonymouse69 Jul 10 '21 at 19:53
46

For Swift 1 and 2 (for Swift 3 see answer by achi using an inout parameter): Argument of a function in Swift is let by default so change it to var if you need to alter the value i.e,

func reduceToZero(var x:Int) -> Int {
    while (x != 0) {
        x = x-1     
    }
    return x
}
Ali Beadle
  • 4,486
  • 3
  • 30
  • 55
LML
  • 1,659
  • 12
  • 29
  • 2
    Why is this answer up-voted like hell? The other answer was placed before this one, and contains more information than this one. – Cristik Dec 15 '15 at 21:48
  • 18
    /!\ Using var will create a copy of the variable passed in parameters. So modifying it won't modify the original value. Also `var` in parameters is very likely to disappear in newer Swift versions per https://github.com/apple/swift-evolution/blob/master/proposals/0003-remove-var-parameters-patterns.md – Matthieu Riegler Dec 17 '15 at 17:04
  • 18
    var keyword in method paremeter will be deprecated in Swift 3. – Boon May 15 '16 at 15:20
  • 4
    I think with Swift 3, we won't be able to do this anymore. We'll have to create a variable copy of the array and return that modified array. – C0D3 May 18 '16 at 16:40
  • This answer is the correct answer: http://stackoverflow.com/questions/24077880/swift-make-method-parameter-mutable/36993835#36993835 – esreli Oct 28 '16 at 03:29
18

Swift3 answer for passing mutable array pointer.

Function:

func foo(array: inout Array<Int>) {
    array.append(1)
}

Call to function:

var a = Array<Int>()
foo(array:&a)
joshd
  • 1,626
  • 14
  • 17
  • Honestly, I'm not sure if this is correct as the Swift ground is ever-changing. I thought this better than doing var array = array inside the function because that makes a copy (and doesn't actually affect the original array structure)? Is a better design approach to aforementioned var approach and then return the new mutated array? – joshd Jul 31 '16 at 06:57
7

In Swift you just add the var keyword before the variable name in the function declaration:

func reduceToZero(var x:Int) -> Int { // notice the "var" keyword
    while (x != 0) {
        x = x-1            
    }
    return x
}

Refer to the subsection "Constant and Variable Parameters" in the "Functions" chapter of the Swift book (page 210 of the iBook as it is today).

DK_
  • 2,648
  • 2
  • 21
  • 20
6

There are some cases where we dont ned to use inout

We can use something like this if you want that changes/scope to be only inside the function:

func manipulateData(a: Int) -> Int {
    var a = a
    // ...
}
dheeru
  • 319
  • 5
  • 6
0

Solution using Swift5 with Functional Programming...

func reduceToZeroFP(x:Int) -> Int {
    x == 0 ? x : reduceToZeroFP(x: x - 1)
}
Jules Burt
  • 115
  • 2
  • 7