Swift¶
See the Swift docs.
Initial observations¶
- Assertions are quite nice
- Specifying the param for function seems quite odd compared to positional calls
- Easy to declare immutable things
- Equivalent of structured bindings is good
// Declare constant
let n = 2
let đ¶ = "woof" // wtf
// Declare variables
var m = 3, o = 3
m = 4
// Type annotations
var welcomeMessage: String
// welcomeMessage = 0 // error
welcomeMessage = "hi"
print(welcomeMessage)
var red, green, blue: Double
red = 1 // initialised here with an integer
print("red is \(red)")
// booleans
let thisisafact = true
if (thisisafact) {
print("fact is \(thisisafact)")
}
// tuples
let codes = (404, "not found")
print(codes)
// partial assign with underscore
let (_, str) = codes
print(str)
let http200Status = (statusCode: 200, description: "OK")
print(http200Status)
// assertions
let x = 4
assert(x > 0, "greater than zero")
precondition(x > 0, "greater than zero")
// Declare function
func square(n: Int) -> Int {
return n * n
}
let result = square(n: 2)
print("result of square is \(result)")
func factorial(n : Int) -> Int {
if (n == 1) {
return 1
}
else {
return n * factorial(n: n - 1)
}
}
assert(factorial(n: 1) == 1)
assert(factorial(n: 2) == 2)
assert(factorial(n: 3) == 6)
assert(factorial(n: 4) == 24)
// String concatenation
func repeater(c : String, n : UInt) -> String {
if (n == 0) {
return String("")
}
if (n == 1) {
return c
}
return c + repeater(c : c, n : n - 1)
}
assert(repeater(c : "x", n : 0) == "")
assert(repeater(c : "x", n : 1) == "x")
assert(repeater(c : "x", n : 4) == "xxxx")
assert(repeater(c : "ab", n : 4) == "abababab")
print(repeater(c : "ab", n : 4))
ChatGPT says¶
This document focuses on the practical, day-to-day differences youâll feel when moving to Swift from a long C++ background. Itâs less about syntax and more about how your mental model of program structure, ownership, and flow will change.
1. Memory Management: You Stop Owning Lifetimes Directly¶
C++ mindset:
- RAII
- Deterministic destruction
- Stack vs heap matters everywhere
- Move/copy semantics are explicit design tools
Swift reality:
- Most reference types use ARC (Automatic Reference Counting)
- You still care about lifetimes, but failure modes shift
Key differences:
- You think in terms of reference cycles (
strong/weak/unowned) instead of deletes - Deinitialization is not tied to lexical scope the way C++ destructors are
- Tight timing assumptions around destruction are discouraged
Important shift: Swift encourages modeling data as value types (struct, enum) to avoid ARC entirely where possible.
2. Value Semantics Are the Default (and Very Powerful)¶
Swift aggressively prefers value types:
structandenumare idiomaticclassis used only when identity or shared mutable state matters
Notable examples:
Array,Dictionary, andStringare value types- They use copy-on-write, so copying is usually cheap
How this feels coming from C++:
- You pass values freely
- You mutate less shared state
- Many designs that would require shared pointers in C++ become simple value transformations
3. Optionals Replace Nulls and Sentinels (and the Compiler Enforces It)¶
Swiftâs Optional is not a conventionâitâs a core language feature.
TandT?are distinct types- You must explicitly unwrap
Common patterns:
if letguard let??(nil-coalescing)switchover optionals
Resulting code shape:
- Early exits (
guard) replace deeply nested conditionals - Error and absence handling becomes explicit and visible
4. Error Handling Is a First-Class Language Feature¶
Swift uses throw / try / catch, and throwing is part of a functionâs type.
Youâll commonly see:
- Throwing functions for exceptional failure
Result<T, Error>for explicit outcome modeling- Optionals where absence is expected and non-exceptional
C++ comparison:
Think of Swift as embracing both exceptions and expected-style APIsâclearly and idiomatically.
5. Protocols + Extensions = Interfaces, Mixins, and Retroactive Modeling¶
Swiftâs protocols go far beyond pure virtual interfaces:
- Default implementations via protocol extensions
- Retroactive conformance (add protocol conformance to existing types)
- Extensions let you add behavior without subclassing
Mental shift: Youâll often reach for:
protocol + extension
Where in C++ you might have used:
- Templates
- Traits
- Base classes
6. Generics Exist â But Arenât a Meta-Programming Playground¶
Swift generics are expressive, but intentionally constrained:
- No SFINAE-style wizardry
- Constraints are explicit (
where T: Protocol) - Emphasis is on clarity over cleverness
The culture favors:
- Readable constraints
- Protocol-oriented abstraction
- Letting the compiler specialize where appropriate
7. Enums Are Real Sum Types (Algebraic Data Types)¶
Swift enums can carry data per case:
enum Token {
case number(Double)
case ident(String)
case eof
}
Features:
- Exhaustive
switch - Pattern matching is central and expressive
- Compiler-enforced completeness
This replaces large amounts of:
- Tagged unions
std::variantboilerplate- Class hierarchies used only for state
8. Mutability Is Explicit and Enforced¶
Swift makes mutation obvious:
let= immutablevar= mutable- Methods mutating value types must be marked
mutating
Effect:
- Fewer hidden side effects
- Easier reasoning about data flow
- Naturally more functional-style code, without forcing purity
9. Concurrency: Structured Async by Default¶
Modern Swift emphasizes:
async / await- Structured concurrency (task trees)
- Actors for isolating mutable state
Compared to:
std::thread- Thread pools
- Futures/promises
- Callback-based designs
Swift strongly nudges you toward correct-by-construction concurrency patterns.
10. Ecosystem and Build Model Differences¶
Apple platforms¶
- Heavy use of reference types
- ARC everywhere
- Event-loop-driven APIs
- Obj-C legacy influences (dynamic dispatch, bridging)
Server-side Swift¶
- Improving but younger ecosystem
- Different expectations around package management and distribution
11. Performance Thinking Still Matters â Just Differently¶
You can write extremely fast Swift, but you optimize different things:
- Prefer value types to reduce ARC traffic
- Avoid unnecessary reference sharing
- Understand copy-on-write triggers
- Be aware of protocol existential costs
Good news: The compiler often makes clean code fast.
Bad news: When it doesnât, you need to know where the sharp edges are.
12. Common Friction Points for C++ Veterans¶
You may initially find yourself thinking:
- âWhy do I have to unwrap everything?â
- âWhy canât I just do pointer arithmetic?â
- âWhere are my deterministic destructors?â
These frustrations usually fade as Swiftâs safety guarantees start paying off.
13. Rough Mental Mapping¶
| C++ Concept | Swift Equivalent |
|---|---|
| Shared-ownership class | class (watch cycles) |
| POD / simple struct | struct |
std::variant |
enum with associated values |
| Interfaces / concepts | protocol (+ extensions) |
unique_ptr |
Value ownership or convention-based class ownership |
shared_ptr graphs |
Reference graphs + weak edges |
Error codes / expected |
throws, Result |
Final Thought¶
Swift is not âC++ with training wheels.â Itâs a language that aggressively encodes correctness, clarity, and intent into the type system.
Once you stop fighting thatâand let value semantics, protocols, and explicit handling leadâyouâll likely find yourself writing less code, with fewer bugs, and a very different sense of confidence about what your program is doing.