Hello World
The minimum runnable program.
print("Hello, World!")console.log("Hello, World!");public class Main {
public static void main(String[] args) {
System.out.println("Hello, World!");
}
}console.log("Hello, World!");package main
import "fmt"
func main() {
fmt.Println("Hello, World!")
}fn main() {
println!("Hello, World!");
}#include <iostream>
int main() {
std::cout << "Hello, World!\n";
}fun main() {
println("Hello, World!")
}print("Hello, World!")#include <stdio.h>
int main(void) {
printf("Hello, World!\n");
return 0;
}Variables and Constants
Declaring mutable variables and immutable constants.
x = 42 # variable (re-assignable)
name = "Alice" # type inferred
PI = 3.14159 # convention: UPPER = constantlet x = 42; // mutable
const name = "Alice"; // immutable binding
const PI = 3.14159;int x = 42; // mutable
String name = "Alice";
final double PI = 3.14; // immutablelet x: number = 42; // explicit type
let name = "Alice"; // inferred: string
const PI = 3.14159; // const — immutablevar x int = 42 // explicit
x := 42 // shorthand (inferred)
name := "Alice"
const PI = 3.14159let x = 42; // immutable
let mut y = 42; // mutable
let name = "Alice";
const PI: f64 = 3.14159;int x = 42;
auto name = std::string("Alice"); // inferred
constexpr double PI = 3.14159; // compile-time constvar x = 42 // mutable (inferred Int)
val name = "Alice" // immutable reference
const val PI = 3.14159 // compile-time constvar x = 42 // mutable (inferred Int)
let name = "Alice" // immutable (let = constant)
let PI = 3.14159int x = 42;
char name[] = "Alice";
const double PI = 3.14159; // const
#define MAX 100 // preprocessor constantFunctions
Defining and calling a function with parameters and return value.
def add(a: int, b: int) -> int:
return a + b
result = add(3, 4) # 7function add(a, b) { return a + b; }
const addArrow = (a, b) => a + b;
add(3, 4); // 7
addArrow(3, 4); // 7static int add(int a, int b) {
return a + b;
}
int result = add(3, 4); // 7function add(a: number, b: number): number {
return a + b;
}
const addArrow = (a: number, b: number): number => a + b;func add(a, b int) int { return a + b }
// Multiple return values (idiomatic)
func divide(a, b float64) (float64, error) {
if b == 0 { return 0, errors.New("div/0") }
return a / b, nil
}fn add(a: i32, b: i32) -> i32 {
a + b // last expression = return value
}
let result = add(3, 4); // 7int add(int a, int b) { return a + b; }
auto addLambda = [](int a, int b) { return a + b; };
add(3, 4); // 7
addLambda(3, 4); // 7fun add(a: Int, b: Int): Int = a + b
// Single-expression shorthand ↑
fun divide(a: Double, b: Double): Double? =
if (b == 0.0) null else a / bfunc add(_ a: Int, _ b: Int) -> Int { a + b }
// _ suppresses argument label at call site
let result = add(3, 4) // 7int add(int a, int b) { return a + b; }
int result = add(3, 4); /* 7 */Classes and Data Structures
Defining a type that groups data and behaviour.
from dataclasses import dataclass
@dataclass
class Point:
x: float
y: float
def distance(self) -> float:
return (self.x**2 + self.y**2)**0.5
p = Point(3.0, 4.0)
print(p.distance()) # 5.0class Point {
#x; #y; // private fields
constructor(x, y) { this.#x = x; this.#y = y; }
distance() { return Math.hypot(this.#x, this.#y); }
get x() { return this.#x; }
}
const p = new Point(3, 4);
p.distance(); // 5record Point(double x, double y) {
double distance() {
return Math.hypot(x, y);
}
}
var p = new Point(3, 4);
p.distance(); // 5.0class Point {
constructor(
private readonly x: number,
private readonly y: number
) {}
distance() { return Math.hypot(this.x, this.y); }
}
const p = new Point(3, 4);
p.distance(); // 5type Point struct { X, Y float64 }
func (p Point) Distance() float64 {
return math.Hypot(p.X, p.Y)
}
p := Point{3, 4}
p.Distance() // 5.0struct Point { x: f64, y: f64 }
impl Point {
fn distance(&self) -> f64 {
(self.x*self.x + self.y*self.y).sqrt()
}
}
let p = Point { x: 3.0, y: 4.0 };
p.distance() // 5.0struct Point {
double x, y;
double distance() const {
return std::hypot(x, y);
}
};
Point p{3.0, 4.0};
p.distance(); // 5.0data class Point(val x: Double, val y: Double) {
fun distance() = Math.hypot(x, y)
}
val p = Point(3.0, 4.0)
p.distance() // 5.0struct Point {
let x: Double
let y: Double
func distance() -> Double { hypot(x, y) }
}
let p = Point(x: 3, y: 4)
p.distance() // 5.0typedef struct { double x, y; } Point;
double distance(const Point *p) {
return sqrt(p->x*p->x + p->y*p->y);
}
Point p = {3.0, 4.0};
distance(&p); /* 5.0 */Null / Optional Values
How each language represents "no value" and forces you to handle it.
name: str | None = None # Optional type hint (Python 3.10+)
length = len(name) if name else 0
result = name or "default" # falsy checklet name = null;
// Optional chaining — no throw
const len = name?.length; // undefined
const display = name ?? "anon"; // nullish coalescingOptional<String> name = Optional.empty();
name.map(String::length) // Optional.empty
.orElse(0); // 0
name.ifPresent(System.out::println);let name: string | null = null;
const len = name?.length; // undefined
const display = name ?? "anon"; // "anon"var name *string = nil
// Check before use
if name != nil { fmt.Println(len(*name)) }
// Comma-ok for maps/channels
val, ok := myMap["key"]
if ok { use(val) }let name: Option<&str> = None;
let len = name.map(|s| s.len()).unwrap_or(0);
if let Some(n) = name { println!("{}", n); }
let display = name.unwrap_or("anon");std::optional<std::string> name = std::nullopt;
auto len = name.has_value() ? name->length() : 0;
auto display = name.value_or("anon");val name: String? = null
val len = name?.length ?: 0 // safe call + Elvis
val display = name ?: "anon"
name?.let { println(it) } // only if non-nulllet name: String? = nil
let len = name?.count ?? 0 // optional chaining + ??
let display = name ?? "anon"
if let n = name { print(n) } // if let bindingchar *name = NULL;
/* Must check manually — no compile enforcement */
if (name != NULL) { printf("%zu\n", strlen(name)); }
const char *display = name ? name : "anon";Error Handling
How each language propagates and handles failures.
try:
result = int("bad")
except ValueError as e:
print(f"Error: {e}")
else:
print("Success:", result) # no exception
finally:
print("always runs")try {
const n = JSON.parse("bad");
} catch (e) {
if (e instanceof SyntaxError) console.error(e.message);
else throw e; // re-throw unexpected errors
}try {
int n = Integer.parseInt("bad");
} catch (NumberFormatException e) {
System.err.println(e.getMessage());
} finally {
System.out.println("cleanup");
}function parse(s: string): Result<number, string> {
const n = Number(s);
return isNaN(n) ? { ok: false, error: "NaN" }
: { ok: true, value: n };
}n, err := strconv.Atoi("bad")
if err != nil {
fmt.Println("error:", err)
return // handle at call site
}
// n is valid herefn parse(s: &str) -> Result<i32, _> {
let n = s.parse::<i32>()?; // ? propagates error
Ok(n)
}
match parse("42") {
Ok(n) => println!("{}", n),
Err(e) => println!("{}", e),
}try {
int n = std::stoi("bad");
} catch (const std::invalid_argument& e) {
std::cerr << e.what();
} catch (const std::exception& e) {
std::cerr << e.what();
}val n = "bad".toIntOrNull() // null on failure (idiomatic)
?: throw IllegalArgumentException("bad input")
// Or: runCatching wraps exceptions
val result = runCatching { "42".toInt() }
result.onSuccess { println(it) }
.onFailure { println(it.message) }do {
let data = try JSONDecoder().decode(User.self, from: raw)
} catch let e as DecodingError {
print("Decode failed: \(e)")
} catch {
print("Unknown: \(error)")
}FILE *f = fopen("file.txt", "r");
if (f == NULL) {
perror("fopen"); /* prints errno message */
return -1;
}
/* use f */
fclose(f);Loops
Iterating over a range and over a collection.
for i in range(10): print(i)
items = ["a", "b", "c"]
for item in items: print(item)
for i, item in enumerate(items): print(i, item)for (let i = 0; i < 10; i++) console.log(i);
for (const item of items) console.log(item);
items.forEach((item, i) => console.log(i, item));for (int i = 0; i < 10; i++) System.out.println(i);
for (String item : items) System.out.println(item);
IntStream.range(0, items.size())
.forEach(i -> System.out.println(i + " " + items.get(i)));for (let i = 0; i < 10; i++) console.log(i);
for (const item of items) console.log(item);
items.forEach((item, i) => console.log(i, item));for i := 0; i < 10; i++ { fmt.Println(i) }
for _, item := range items { fmt.Println(item) }
for i, item := range items { fmt.Println(i, item) }for i in 0..10 { println!("{}", i); }
for item in &items { println!("{}", item); }
for (i, item) in items.iter().enumerate() {
println!("{} {}", i, item);
}for (int i = 0; i < 10; i++) std::cout << i << "\n";
for (const auto& item : items) std::cout << item << "\n";
for (size_t i = 0; i < items.size(); i++)
std::cout << i << " " << items[i] << "\n";for (i in 0..9) println(i) // inclusive
for (i in 0 until 10) println(i) // exclusive
for ((i, item) in items.withIndex()) println("$i $item")for i in 0..<10 { print(i) } // half-open
for item in items { print(item) }
for (i, item) in items.enumerated() { print(i, item) }for (int i = 0; i < 10; i++) printf("%d\n", i);
/* Array iteration — must know size */
for (int i = 0; i < n; i++) printf("%s\n", items[i]);Arrays and Lists
Dynamic arrays / lists — create, append, access, slice.
nums = [1, 2, 3]
nums.append(4) # [1, 2, 3, 4]
nums[0] # 1
nums[-1] # 4 (last)
nums[1:3] # [2, 3] (slice)
len(nums) # 4const nums = [1, 2, 3];
nums.push(4); // [1, 2, 3, 4]
nums[0]; // 1
nums.at(-1); // 4 (last)
nums.slice(1, 3); // [2, 3]
nums.length; // 4var nums = new ArrayList<>(List.of(1, 2, 3));
nums.add(4); // [1, 2, 3, 4]
nums.get(0); // 1
nums.get(nums.size()-1); // 4
nums.subList(1, 3); // [2, 3]
nums.size(); // 4nums := []int{1, 2, 3}
nums = append(nums, 4) // [1 2 3 4]
nums[0] // 1
nums[len(nums)-1] // 4
nums[1:3] // [2 3]
len(nums) // 4let mut nums = vec![1, 2, 3];
nums.push(4); // [1, 2, 3, 4]
nums[0]; // 1
nums.last(); // Some(4)
&nums[1..3]; // &[2, 3] (slice)
nums.len(); // 4std::vector<int> nums{1, 2, 3};
nums.push_back(4); // [1, 2, 3, 4]
nums[0]; // 1
nums.back(); // 4
// No direct slice — use iterators
nums.size(); // 4val nums = mutableListOf(1, 2, 3)
nums.add(4) // [1, 2, 3, 4]
nums[0] // 1
nums.last() // 4
nums.subList(1, 3) // [2, 3]
nums.size // 4var nums = [1, 2, 3]
nums.append(4) // [1, 2, 3, 4]
nums[0] // 1
nums.last // Optional(4)
nums[1...2] // ArraySlice: [2, 3]
nums.count // 4int nums[] = {1, 2, 3}; /* fixed-size */
int n = 3;
/* No append — must realloc for dynamic */
nums[0]; /* 1 */
nums[n-1]; /* 3 (last) */
sizeof(nums)/sizeof(nums[0]); /* 3 */Maps and Dictionaries
Key-value data structures — create, get, set, iterate.
d = {"name": "Alice", "age": 30}
d["email"] = "a@b.c" # set
d.get("name") # "Alice"
d.get("x", "default") # "default"
for k, v in d.items(): print(k, v)const m = new Map([["name","Alice"],["age",30]]);
m.set("email", "a@b.c");
m.get("name"); // "Alice"
for (const [k, v] of m) console.log(k, v);
// Object literal: { name: "Alice", age: 30 }var m = new HashMap<>(Map.of("name","Alice","age",30));
m.put("email", "a@b.c");
m.get("name"); // "Alice"
m.getOrDefault("x", "def"); // "def"
m.forEach((k, v) -> System.out.println(k + ": " + v));m := map[string]any{"name": "Alice", "age": 30}
m["email"] = "a@b.c"
v, ok := m["name"] // comma-ok: check existence
for k, v := range m { fmt.Println(k, v) }use std::collections::HashMap;
let mut m = HashMap::from([("name","Alice")]);
m.insert("email", "a@b.c");
m.get("name"); // Some("Alice")
m.get("x").unwrap_or("def");
for (k, v) in &m { println!("{}: {}", k, v); }std::unordered_map<std::string,int> m;
m["age"] = 30;
m.at("age"); // 30 (throws if missing)
m.count("age"); // 1 if exists
for (auto& [k,v] : m) std::cout << k << ": " << v;val m = mutableMapOf("name" to "Alice", "age" to 30)
m["email"] = "a@b.c"
m["name"] // "Alice"
m.getOrDefault("x","def")
m.forEach { (k,v) -> println("$k: $v") }var m: [String: Any] = ["name":"Alice","age":30]
m["email"] = "a@b.c"
m["name"] // Optional("Alice")
m["x", default: "def"] // "def"
for (k, v) in m { print("\(k): \(v)") }/* No built-in map — manual approach */
typedef struct { char key[64]; int value; } KV;
KV map[] = {{"name",0},{"age",30}};
/* Or use a hash table library like uthash */Closures / Anonymous Functions
Functions that capture variables from their surrounding scope.
def make_adder(n):
return lambda x: x + n # captures n
add5 = make_adder(5)
add5(3) # 8const makeAdder = (n) => (x) => x + n;
const add5 = makeAdder(5);
add5(3); // 8Function<Integer,Integer> makeAdder(int n) {
return x -> x + n; // n must be effectively final
}
var add5 = makeAdder(5);
add5.apply(3); // 8const makeAdder = (n: number) => (x: number): number => x + n;
const add5 = makeAdder(5);
add5(3); // 8func makeAdder(n int) func(int) int {
return func(x int) int { return x + n }
}
add5 := makeAdder(5)
add5(3) // 8fn make_adder(n: i32) -> impl Fn(i32) -> i32 {
move |x| x + n // move: capture n by value
}
let add5 = make_adder(5);
add5(3); // 8auto makeAdder = [](int n) {
return [n](int x) { return x + n; }; // captures n by value
};
auto add5 = makeAdder(5);
add5(3); // 8fun makeAdder(n: Int): (Int) -> Int = { x -> x + n }
val add5 = makeAdder(5)
add5(3) // 8func makeAdder(_ n: Int) -> (Int) -> Int {
return { x in x + n } // n captured
}
let add5 = makeAdder(5)
add5(3) // 8/* No closures — simulate with function pointer + context */
typedef struct { int n; } Adder;
int add(Adder *ctx, int x) { return ctx->n + x; }
Adder ctx = {5};
add(&ctx, 3); /* 8 */Async / Concurrent Operations
Running non-blocking I/O and waiting for results.
import asyncio
async def fetch(id: int) -> str:
await asyncio.sleep(0.1)
return f"data-{id}"
async def main():
# Concurrent: both run at the same time
a, b = await asyncio.gather(fetch(1), fetch(2))
print(a, b)
asyncio.run(main())async function fetch(id) {
await new Promise(r => setTimeout(r, 100));
return `data-${id}`;
}
const [a, b] = await Promise.all([fetch(1), fetch(2)]);
console.log(a, b);CompletableFuture<String> f1 = CompletableFuture
.supplyAsync(() -> "data-1");
CompletableFuture<String> f2 = CompletableFuture
.supplyAsync(() -> "data-2");
CompletableFuture.allOf(f1, f2).join();
System.out.println(f1.get() + " " + f2.get());async function fetch(id: number): Promise<string> {
await new Promise(r => setTimeout(r, 100));
return `data-${id}`;
}
const [a, b] = await Promise.all([fetch(1), fetch(2)]);func fetch(id int) string {
time.Sleep(100 * time.Millisecond)
return fmt.Sprintf("data-%d", id)
}
// Concurrent with goroutines + channel
ch := make(chan string, 2)
go func() { ch <- fetch(1) }()
go func() { ch <- fetch(2) }()
a, b := <-ch, <-chasync fn fetch(id: u32) -> String {
tokio::time::sleep(Duration::from_millis(100)).await;
format!("data-{}", id)
}
let (a, b) = tokio::join!(fetch(1), fetch(2));
println!("{} {}", a, b);auto f1 = std::async(std::launch::async,
[]{ return std::string("data-1"); });
auto f2 = std::async(std::launch::async,
[]{ return std::string("data-2"); });
std::cout << f1.get() << " " << f2.get();suspend fun fetch(id: Int): String {
delay(100)
return "data-$id"
}
// Concurrent with async
val (a, b) = coroutineScope {
async { fetch(1) } to async { fetch(2) }
}.let { (a, b) -> a.await() to b.await() }func fetch(id: Int) async -> String {
try? await Task.sleep(nanoseconds: 100_000_000)
return "data-\(id)"
}
async let a = fetch(id: 1) // concurrent
async let b = fetch(id: 2) // concurrent
let (ra, rb) = await (a, b)#include <pthread.h>
void *worker(void *arg) {
/* do work */
return "result";
}
pthread_t t1, t2;
pthread_create(&t1, NULL, worker, NULL);
pthread_create(&t2, NULL, worker, NULL);
pthread_join(t1, NULL);
pthread_join(t2, NULL);Generics / Type Parameters
Writing functions and types that work for any type.
from typing import TypeVar
T = TypeVar("T")
def first(items: list[T]) -> T | None:
return items[0] if items else None
first([1, 2, 3]) # 1 (int)
first(["a", "b"]) # "a" (str)// No generics — dynamic typing
function first(items) {
return items.length > 0 ? items[0] : null;
}
first([1, 2, 3]); // 1
first(["a", "b"]); // "a"<T> Optional<T> first(List<T> items) {
return items.isEmpty() ? Optional.empty()
: Optional.of(items.get(0));
}
first(List.of(1, 2, 3)); // Optional[1]
first(List.of("a", "b")); // Optional[a]function first<T>(items: T[]): T | undefined {
return items[0];
}
first([1, 2, 3]); // 1 — inferred: number
first(["a", "b"]); // "a" — inferred: stringfunc First[T any](items []T) (T, bool) {
if len(items) == 0 {
var zero T; return zero, false
}
return items[0], true
}
First([]int{1, 2, 3}) // 1, true
First([]string{"a", "b"}) // "a", truefn first<T>(items: &[T]) -> Option<&T> {
items.first()
}
first(&[1, 2, 3]); // Some(1)
first(&["a", "b"]); // Some("a")template<typename T>
std::optional<T> first(const std::vector<T>& v) {
return v.empty() ? std::nullopt : std::optional(v[0]);
}
first(std::vector{1,2,3}); // 1
first(std::vector{"a"s,"b"s}); // "a"fun <T> first(items: List<T>): T? = items.firstOrNull()
first(listOf(1, 2, 3)) // 1
first(listOf("a", "b")) // "a"func first<T>(_ items: [T]) -> T? { items.first }
first([1, 2, 3]) // Optional(1)
first(["a", "b"]) // Optional("a")/* No generics — use void* with size parameter */
void* first(void *arr, size_t elem_size, size_t count) {
return count > 0 ? arr : NULL;
}
int nums[] = {1,2,3};
int *f = first(nums, sizeof(int), 3); /* 1 */Pattern Matching
Branching on the shape or type of a value.
match command:
case "quit": exit()
case "help" | "?": show_help()
case ["go", dir]: move(dir)
case {"action": a}: handle(a)
case _: print("unknown")switch (command) {
case "quit": exit(); break;
case "help": showHelp(); break;
default: console.log("unknown");
}
// Or discriminated union pattern with if/typeofString result = switch (shape) {
case Circle c -> "circle r=" + c.radius();
case Rectangle r -> "rect " + r.w() + "x" + r.h();
case Triangle t -> "triangle";
// exhaustive — no default needed
};// Discriminated union
function handle(msg: Message): string {
switch (msg.kind) {
case "quit": return "bye";
case "move": return `move to ${msg.dir}`;
case "write": return msg.text;
}
}switch v := i.(type) {
case int: fmt.Println("int:", v)
case string: fmt.Println("string:", v)
case bool: fmt.Println("bool:", v)
default: fmt.Println("other")
}match value {
0 => println!("zero"),
1..=9 => println!("single digit"),
n if n < 0 => println!("negative: {}", n),
n => println!("large: {}", n),
}std::visit([](auto&& arg) {
using T = std::decay_t<decltype(arg)>;
if constexpr (std::is_same_v<T, int>)
std::cout << "int: " << arg;
else if constexpr (std::is_same_v<T, std::string>)
std::cout << "string: " << arg;
}, myVariant);when (value) {
is Int -> println("int: $value")
is String -> println("string: $value")
in 1..10 -> println("in range")
null -> println("null")
else -> println("other")
}switch value {
case 0: print("zero")
case 1..<10: print("small")
case let n where n < 0: print("negative \(n)")
case is String: print("it's a String")
default: print("other")
}switch (value) {
case 0: printf("zero\n"); break;
case 1: printf("one\n"); break;
default: printf("other\n");
}
/* Strings: use if/strcmp chain */String Interpolation
Embedding values inside string literals.
name, age = "Alice", 30
f"Hello {name}, you are {age}!"
f"{'upper'.upper()}" # expressions workconst name = "Alice", age = 30;
`Hello ${name}, you are ${age}!`
`${age >= 18 ? "adult" : "minor"}` // expressions// Text block + formatted (Java 15+)
String.format("Hello %s, you are %d!", name, age);
// Java 21 string templates (preview):
// STR."Hello \{name}, you are \{age}!"const name = "Alice", age = 30;
`Hello ${name}, you are ${age}!`
`${age >= 18 ? "adult" : "minor"}`name, age := "Alice", 30
fmt.Sprintf("Hello %s, you are %d!", name, age)
// No native interpolation — use fmt.Sprintflet name = "Alice"; let age = 30;
format!("Hello {name}, you are {age}!")
// Named directly in {} since Rust 1.58auto name = "Alice"; int age = 30;
std::format("Hello {}, you are {}!", name, age) // C++20
// Before C++20: ostringstream or snprintfval name = "Alice"; val age = 30
"Hello $name, you are $age!"
"${name.uppercase()} — ${if (age>=18) "adult" else "minor"}"let name = "Alice"; let age = 30
"Hello \(name), you are \(age)!"
"\(age >= 18 ? "adult" : "minor")"char buf[128];
snprintf(buf, sizeof(buf), "Hello %s, age %d!", name, age);
/* snprintf — safe; never use sprintf */Type System Snapshot
Key characteristics of each language's type system.
Dynamic, duck-typed.
Types checked at runtime.
Type hints (PEP 484) are optional
annotations — enforced by mypy/pyright
not the interpreter.
No compile-time type checking.Dynamic, weakly typed.
Coercions: "5" - 1 == 4
"5" + 1 == "51"
typeof null === "object" (historical bug)
No compile-time checks.Static, nominal, strong.
Checked at compile time.
Type erasure: List<String> and List<Integer>
are the same at runtime.
Autoboxing: int ↔ Integer.Static, structural typing.
Types erased at compile time.
string is a subtype of {} and any.
strict: true required for full safety.
any disables type checking entirely.Static, structural interfaces.
Interfaces satisfied implicitly.
No inheritance — only composition.
Generics added in 1.18 (2022).
Strong: no implicit coercion.Static, nominal, affine types.
Ownership enforced at compile time.
No null — Option<T> instead.
No exceptions — Result<T,E>.
Lifetimes: references always valid.Static, nominal.
Templates: compile-time reification.
RTTI available but slow — often avoided.
Implicit conversions can cause bugs.
Concepts (C++20) constrain templates.Static, nominal, null-safe.
Nullable types (T?) in the type system.
Smart casts after null/type checks.
Structural typing via interfaces.
Reified generics in inline functions.Static, nominal, protocol-oriented.
Optional<T> (T?) is an enum, not null.
Value types (struct/enum) preferred.
Generics: compile-time specialisation.
Sendable: type-level concurrency safety.Static, nominal, weakly typed.
Implicit conversions between numerics.
void*: type-unsafe generic pointer.
No generics — templates, macros instead.
Signed overflow: undefined behaviour.Memory Management
How each language manages object lifetimes and memory.
Reference counting + cycle-detecting GC.
CPython: GIL limits true parallelism.
Memory freed when refcount hits 0.
del removes a reference, not the object.
Finalizers (__del__) are unreliable.Tracing GC (V8: generational).
Non-deterministic collection.
WeakRef / FinalizationRegistry for
weak references (ES2021).
No manual control.Generational GC (G1, ZGC, Shenandoah).
Stop-the-world pauses (minimised by ZGC).
Stack: primitives + references.
Heap: all objects.
try-with-resources for deterministic close.Tri-color mark-and-sweep GC.
Very low pause times (<1ms target).
Escape analysis: compiler moves to heap
only what must escape the stack.
runtime.GC() for manual trigger.Ownership + borrow checker at compile time.
Freed when owner goes out of scope (Drop).
No GC pauses — deterministic deallocation.
Rc<T>: shared (single-threaded).
Arc<T>: shared (multi-threaded).RAII: resources freed when object destroyed.
unique_ptr: sole ownership (no overhead).
shared_ptr: shared via atomic refcount.
Stack objects: freed at scope exit.
new/delete: manual (avoid in modern C++).JVM GC (same as Java).
use { } for AutoCloseable resources.
Primitive types (Int, Double) compiled
to JVM primitives where possible.
No manual memory management.Automatic Reference Counting (ARC).
Deterministic — freed when refcount hits 0.
No GC pauses.
weak/unowned: break retain cycles.
Value types (struct): copied on assignment.Fully manual: malloc/free.
Stack: local variables, freed on return.
Heap: malloc/calloc — must free exactly once.
No GC, no RAII, no smart pointers.
Buffer overflow, UAF: undefined behaviour.Modules and Imports
Organising code across files and importing from other modules.
import os
import os.path
from pathlib import Path
from typing import Optional, List
# Relative import (inside package)
from .utils import helperimport { add } from "./math.mjs"; // named
import Calculator from "./calc.mjs"; // default
import * as math from "./math.mjs"; // namespace
const lazy = await import("./heavy.mjs"); // dynamicimport java.util.List;
import java.util.Optional;
import java.util.*; // wildcard
import static Math.PI; // static importimport { add } from "./math";
import type { User } from "./types"; // type-only import
export { add, multiply };
export default class Calculator {}import (
"fmt"
"math"
"github.com/user/pkg" // external module
m "mymodule/math" // alias
_ "mymodule/init" // side-effect only
)use std::collections::HashMap;
use std::io::{self, Read, Write};
// External: declare in Cargo.toml, then:
use serde::{Deserialize, Serialize};
pub mod utils; // declare a module#include <vector>
#include <string>
#include "myheader.hpp"
// C++20 modules:
import std;
import mymodule;import kotlin.math.*
import java.util.Optional
import com.example.MyClass
import com.example.MyClass as Aliasimport Foundation
import UIKit
import SwiftUI
// Internal files: same module = visible
// No explicit import needed for same module#include <stdio.h> // system header
#include <stdlib.h>
#include "myheader.h" // local header
// No namespace — everything is globalConcurrency Model
How each language approaches concurrent and parallel execution.
asyncio: single-thread cooperative (I/O).
threading: OS threads — GIL limits CPU.
multiprocessing: true parallelism (no GIL).
concurrent.futures: high-level thread/process pool.
GIL: Global Interpreter Lock (CPython only).Single-threaded event loop.
async/await: cooperative, non-blocking I/O.
Promises: asynchronous values.
Web Workers / Worker Threads: separate threads.
No shared mutable state between workers.OS threads (1:1).
Virtual threads (Java 21): M:N — millions possible.
CompletableFuture: async composition.
synchronized / locks: mutual exclusion.
Structured concurrency (preview, Java 21).Goroutines: M:N (GMP scheduler).
~2KB stack each — millions possible.
Channels: communicate by sharing.
sync.Mutex / sync.RWMutex for shared state.
context: cancellation propagation.OS threads (std::thread) — no data races.
Compile-time: Send + Sync enforce thread safety.
async/await: cooperative (Tokio/async-std).
Arc<Mutex<T>>: shared mutable across threads.
Rayon: data-parallel iterators.OS threads (std::thread).
std::mutex, std::atomic.
std::async / std::future: task-based.
Coroutines (C++20): stackless cooperative.
No compile-time data-race prevention.Coroutines: M:N via CPS transformation.
Dispatchers: Main, IO, Default.
StateFlow / SharedFlow: reactive streams.
Structured concurrency: CoroutineScope.
Channel: coroutine communication.async/await: cooperative (5.5+).
Actors: compile-time isolation.
@MainActor: main thread guarantee.
Sendable: type-level thread safety (Swift 6).
Cooperative thread pool: bounded size.POSIX threads (pthreads).
pthread_mutex_t for locking.
No compile-time safety — races are UB.
_Atomic / stdatomic.h for atomic ops.
OpenMP for parallel computation.