2024-10-03 (last edit: 2024-09-20)
Even if you don't end up using Rust, learning it expands your horizons
Let's compare the same code written in C, C++ and Rust.
#include <iostream>
#include <unordered_map>
using name = std::string;
using age = int;
using person = std::pair<name, age>;
using address = std::string;
using address_book = std::unordered_map<person, address>;
void print_address_book(const address_book &book)
{
for (const auto &[person, address] : book)
{
std::cout << person.first << " is " << person.second << " years old and lives at " << address << std::endl;
}
}
int main()
{
address_book people{};
people.insert({{"John", 20}, "221B Baker Street, London"});
people.insert({{"Mary", 30}, "Avenue des Champs-Élysées, Paris"});
people.insert({{"Jack", 73}, "Wall Street, New York"});
print_address_book(people);
return 0;
}
(Download the source code for this example: tudruj.cpp)
# sample 1 - different ways of removing elements from the list while iterating
list1 = [1, 2, 3, 4]
for idx, item in enumerate(list1):
del item
list1
# [1, 2, 3, 4]
list2 = [1, 2, 3, 4]
for idx, item in enumerate(list2):
list2.remove(item)
list2
# [2, 4]
list3 = [1, 2, 3, 4]
for idx, item in enumerate(list3[:]):
list3.remove(item)
list3
# []
list4 = [1, 2, 3, 4]
for idx, item in enumerate(list4):
list4.pop(idx)
list4
# [2, 4]
# sample 2 - string interning
a = "abc"
b = "abc"
a is b
# True
a = ''.join(['a', 'b', 'c'])
b = ''.join(['a', 'b', 'c'])
a is b
# False
a = "abc!"
b = "abc!"
a is b
# False
# sample 3 - chained operations
(False == False) in [False]
# False
False == (False in [False])
# False
False == False in [False] # unexpected...
# True
# sample 4 - is operator
a = 256
b = 256
a is b
# True
a = 257
b = 257
a is b
# False
a, b = 257, 257
a is b
# True
257 is 257
# <>:1: SyntaxWarning: "is" with a literal. Did you mean "=="?
# <>:1: SyntaxWarning: "is" with a literal. Did you mean "=="?
# C:\Users\kgasinsk\AppData\Local\Temp\ipykernel_15776\331119389.py:1: SyntaxWarning: "is" with a literal. Did you mean "=="?
# 257 is 257
# sample 5 - local variables
def f(trufel):
if trufel:
y = 1
y += 1
f(True) # everything is fine
f(False) # gives error: local variable 'y' referenced before assignment
# ---------------------------------------------------------------------------
# UnboundLocalError Traceback (most recent call last)
# Input In [17], in <cell line: 1>()
# ----> 1 f(False)
# Input In [15], in f(trufel)
# 3 if trufel:
# 4 y = 1
# ----> 5 y += 1
# UnboundLocalError: local variable 'y' referenced before assignment
(Download the source code for this example: gasinska.py)
// mutowalność jest wbudowana w język
type S struct {
A string
B []string
}
func main() {
x := S{"x-A", []string{"x-B"}}
y := x // copy the struct
y.A = "y-A"
y.B[0] = "y-B"
fmt.Println(x, y)
// Outputs "{x-A [y-B]} {y-A [y-B]}" -- x was modified!
}
// slices i kwestia append
func doStuff(value []string) {
fmt.Printf("value=%v\n", value)
value2 := value[:]
value2 = append(value2, "b")
fmt.Printf("value=%v, value2=%v\n", value, value2)
value2[0] = "z"
fmt.Printf("value=%v, value2=%v\n", value, value2)
}
func main() {
slice1 := []string{"a"} // length 1, capacity 1
doStuff(slice1)
// Output:
// value=[a] -- ok
// value=[a], value2=[a b] -- ok: value unchanged, value2 updated
// value=[a], value2=[z b] -- ok: value unchanged, value2 updated
slice10 := make([]string, 1, 10) // length 1, capacity 10
slice10[0] = "a"
doStuff(slice10)
// Output:
// value=[a] -- ok
// value=[a], value2=[a b] -- ok: value unchanged, value2 updated
// value=[z], value2=[z b] -- WTF?!? value changed???
}
// error handling
len, err := reader.Read(bytes)
if err != nil {
if err == io.EOF {
// All good, end of file
} else {
return err
}
}
// interfejs nil
type Explodes interface {
Bang()
Boom()
}
// Type Bomb implements Explodes
type Bomb struct {}
func (*Bomb) Bang() {}
func (Bomb) Boom() {}
func main() {
var bomb *Bomb = nil
var explodes Explodes = bomb
println(bomb, explodes) // '0x0 (0x10a7060,0x0)'
if explodes != nil {
println("Not nil!") // 'Not nil!' What are we doing here?!?!
explodes.Bang() // works fine
explodes.Boom() // panic: value method main.Bomb.Boom called using nil *Bomb pointer
} else {
println("nil!") // why don't we end up here?
}
}
// ubogie struktury danych, takie customowe tracą type safety m.in poprzez castowanie do interface{}
// kiedyś brak generyków, choć teraz w znacznym stopniu problem został rozwiązany.
(Download the source code for this example: koszowski.go)
def add_contents(input_list, contents=[]):
for val in input_list:
contents.append(val)
return contents
print(add_contents([1])) # [1]
print(add_contents([2])) # [1, 2]
(Download the source code for this example: grodzicki.py)
cargo clippy
(for static analysis)cargo check
, but it's less powerful than clippycargo fmt
(for code formatting)fn main() {
let name = "World";
println!("Hello, {}!", name); // using the println! macro
}
(Download the source code for this example: hello_world.rs)
#![allow(unused_variables)]
#![allow(unused_assignments)]
fn main() {
let x = 40; // inferred type
let y: i32 = 100; // specified type
{
let x = 40 + 2; // shadowing
println!("x is {}", x); // prints 42
}
// x = 0; // compilation error, variables are by default immutable
let mut x = 40; // declare as mutable
x = 0; // now we can reassign
x += 1; // x = x + 1
}
(Download the source code for this example: variables.rs)
#![allow(unused_variables)]
fn main() {
let x = 42;
if x == 42 {
println!("x is 42");
} else if x == 43 {
println!("x is 43");
} else {
println!("x is not 42 or 43");
}
// we can also use ifs as expressions
let a_or_b = if x == 0 {
"a" // notice no semicolon at the end
} else {
"b"
};
}
(Download the source code for this example: conditionals.rs)
#![allow(unused_variables)]
fn main() {
for i in 0..10 {
println!("i is {}", i); // i in [0, 10)
}
let mut x = 0;
while x < 50 {
x += 1;
}
let mut y = 0;
let mut iterations = 0;
loop {
iterations += 1;
if iterations % 2 == 0 {
continue;
}
y += 1;
if y == 10 {
break;
}
}
// we can use labels to refer to a specific loop
let mut count = 0;
'counting_up: loop {
let mut remaining = 10;
loop {
if remaining == 9 {
break;
}
if count == 2 {
break 'counting_up; // ends the outer loop
}
remaining -= 1;
}
count += 1;
}
// We can use break with a value.
// Because loops are expressions too,
// the value we break with will be returned from the functions
let mut counter = 0;
let value = loop {
counter += 1;
if counter == 10 {
break 32;
}
};
}
(Download the source code for this example: loops.rs)
fn get_5() -> u32 {
5 // we could also write "return 5;"
}
fn print_sum(a: u32, b: u32) {
println!("a + b = {}", a + b);
}
fn main() {
let a = 100;
print_sum(a, get_5());
}
(Download the source code for this example: functions.rs)
Click here