2
votes

Why doesn't the following code work? And what do I need to change to make it work?

//: Playground - noun: a place where people can play

import Cocoa

struct Person: CustomDebugStringConvertible, Hashable {
    let name: String
    let age: Int

    // MARK: CustomDebugStringConvertible

    var debugDescription: String {
        return "\(name) is \(age) years old"
    }

    // MARK: Hashable

    var hashValue: Int {
        return name.hashValue ^ age.hashValue
    }
}

func ==(lhs: Person, rhs: Person) -> Bool {
    return lhs.name == rhs.name && lhs.age == rhs.age
}

let ilse = Person(name: "Ilse", age: 33)
let mark = Person(name: "Mark", age: 38)

extension Collection where Iterator.Element: Person {
    var averageAge: Int {
        let sum = self.reduce(0) { $0 + $1.age }
        let count = self.count as! Int
        return sum / count
    }
}

var people = [Person]()
people.append(ilse)
people.append(mark)

let averageAge = people.averageAge

I figured out that if I make the struct a Swift class it works. Does it have something to do with the struct being a value type? I do see a compiler error on the last line. "'[Person]' is not convertible to '<>'"

Thank you.

2

2 Answers

3
votes
extension Collection where Iterator.Element: Person

restricts Iterator.Element to types which adopt the protocol Person or are a subclass of Person. Both is not possible with struct Person, and in the full compiler log you'll find

error: type 'Iterator.Element' constrained to non-protocol type 'Person'

What you probably mean is

extension Collection where Iterator.Element == Person 

which restricts the extension to collections of Person. Alternatively, define a protocol

protocol HasAge {
    var age: Int { get }
}

adopt that by Person

struct Person: CustomDebugStringConvertible, Hashable, HasAge { ... }

and define the extension for collections of elements which have a age:

extension Collection where Iterator.Element: HasAge { ... }
1
votes

Change your Collection's extension to this

extension Collection where Iterator.Element == Person {
    var averageAge: Int {
        let sum = self.reduce(0) { $0 + $1.age }
        let count = self.count as! Int
        return sum / count
    }
}