Menu
Coddy logo textTech

Generic Types

Part of the Object Oriented Programming section of Coddy's Swift journey. Lesson 35 of 57.

Types can be generic too. Stack<Element> works for any element type, the same way Swift's own Array<Element> does. The placeholder is used for properties and method signatures:

struct Stack<Element> {
    private var items: [Element] = []

    mutating func push(_ item: Element) { items.append(item) }
    mutating func pop() -> Element? { items.popLast() }
    var top: Element? { items.last }
    var isEmpty: Bool { items.isEmpty }
}

var numbers = Stack<Int>()
numbers.push(3)
numbers.push(9)
print(numbers.pop()!)

var words = Stack<String>()
words.push("hi")
print(words.top!)

Output:

9
hi

Each concrete version is its own type: a Stack<Int> only accepts integers, and the compiler rejects anything else. Often the type can be inferred from the initial value:

var s = Stack<Int>()
s.push(4)
// s.push("four")      // error: cannot convert String to Int
let names = ["a", "b"]  // inferred as Array<String>

A generic type can have several placeholders and constraints:

struct Pair<First, Second> {
    let first: First
    let second: Second
    func swapped() -> Pair<Second, First> { Pair<Second, First>(first: second, second: first) }
}

struct Leaderboard<Entry: Comparable> {
    var entries: [Entry] = []
    mutating func add(_ e: Entry) { entries.append(e); entries.sort(by: >) }
}

let p = Pair(first: "age", second: 36).swapped()
print(p.first)
var board = Leaderboard<Int>()
board.add(5)
board.add(12)
print(board.entries)

Output:

36
[12, 5]

A constrained extension adds members only when the placeholder meets a condition, such as a sum for stacks of integers:

struct Stack<Element> {
    var items: [Element] = []
    mutating func push(_ item: Element) { items.append(item) }
}

extension Stack where Element == Int {
    var sum: Int { items.reduce(0, +) }
}

var s = Stack<Int>()
s.push(4)
s.push(6)
print(s.sum)

Output:

10
challenge icon

Challenge

Easy

Write the generic struct History<Item> that remembers the last limit items. add(_:) appends an item and drops the oldest when there are more than limit; latest returns the newest item (optional); all returns the items from oldest to newest. Add a constrained extension for Item == Int with an average property (integer division, 0 when empty).

The supplied code reads the limit, a line of words and a line of numbers. It fills a History<String> and a History<Int> and prints the kept words, the latest word, the kept numbers and their average.

Your code goes in History.swift. main.swift holds the supplied input/output code and cannot be edited.

Try it yourself

// Supplied input/output code: keep it as it is
var input: [String] = []
while let line = readLine() { input.append(line) }
let limit = Int(input[0])!
var words = History<String>(limit: limit)
for w in input[1].split(separator: ",") { words.add(String(w)) }
var numbers = History<Int>(limit: limit)
for n in input[2].split(separator: ",") { numbers.add(Int(n)!) }
print(words.all.joined(separator: " "))
print(words.latest ?? "none")
print(numbers.all.map { String($0) }.joined(separator: " "))
print(numbers.average)
quiz iconTest yourself

This lesson includes a short quiz. Start the lesson to answer it and track your progress.

All lessons in Object Oriented Programming

Practice on your own: Swift playground