30

I have a Human class with a function that takes any amount of people and determines if someone is older than any of those people, then returns an array with the people he/she is older than.

func isOlderThan(people: Human...) -> [Human] {
    var p: [Human]

    for person in people {
        if age > person.age {
            p.append(person)
        }
    }
    return p
}

However at

p.append(person)

I'm getting the error

Variable p passed by reference before being initialized

Anyone sure why this is? Thanks!

Brejuro
  • 3,421
  • 8
  • 34
  • 61
  • 2
    And if you don't _upvote_ nor _accept_ answers, people will be less motivated to help you - rewards acting as they do. – GoZoner Jul 19 '14 at 16:14

3 Answers3

75

Your declaration of p is just that, a declaration. You haven't initialised it. You need to change it to

var p = [Human]()

Or, as @MartinR points out,

var p: [Human] = []

There are other equivalent constructs, too, but the important thing is you have to assign something to the declared variable (in both cases here, an empty array that will accept Human members).

Update For completeness, you could also use:

var p: Array<Human> = []

or

var p = Array<Human>()
Grimxn
  • 22,115
  • 10
  • 72
  • 85
  • but why does it say: " passed by reference"? – mfaani Feb 05 '19 at 22:06
  • @Honey I'm not a compiler-level guru, but I expect that the way the `.append` method is implemented in `Array` uses a reference to `p` to change it. – Grimxn Feb 06 '19 at 12:38
2

append is a mutating method of the struct Array.

You can think of a struct method as a function with a hidden self parameter. By default, parameters to functions are constants, and therefore you cannot do anything to it that you cannot do to a constant. A mutating struct method needs to do things that cannot be done to a constant, therefore, it must take the self parameter as inout, i.e. as if it's passed by reference. Thus, you can only call a mutating method on something you can pass by reference. And you cannot pass an uninitialized variable by reference.

newacct
  • 119,665
  • 29
  • 163
  • 224
  • but why does it say: " passed by reference"? – mfaani Feb 05 '19 at 22:06
  • 1
    @Honey: The compiler probably treats struct method calls as function calls, which means it treats mutating struct function calls as functions calls with a parameter passed by reference. – newacct Feb 06 '19 at 02:27
  • hmmm...you mean to the compiler `person.updateAddress(to:newAddress)` is treated as something like: `Person.updateAddress(person:&person, to:newAddress)`? – mfaani Feb 06 '19 at 14:45
  • @Honey: basically – newacct Feb 11 '19 at 07:36
1

You are better off simply using the filter method:

func isOlderThan(people: Human...) -> [Human] {
  return people.filter { self.age > $0.age }
}

It works like this:

 20> class Human {
 21.     let age : Int
 22.     init (age: Int) { self.age = age }
 23.     func isOlderThan (people: Human...) -> [Human] {
 24.         return people.filter { self.age > $0.age }
 25.     }
 26. }

 28> let me = Human(age:27)
me: Human = {
  age = 27
}

 29> me.isOlderThan (Human(age:25), Human(age:30))
$R10: [Human] = 1 value {
  [0] = {
    age = 25
  }
}

By the way, before long you'll find it useful to define an additional method specified as:

func isOlderThan(people: [Human]) -> [Human] { ... }

because as soon as you have an array and since there is no apply method yet in Swift, your original method won't work.

GoZoner
  • 67,920
  • 20
  • 95
  • 145