Rainer Stropek | @rstropek
Image by
Francis C. Franklin / CC-BY-SA-3.0
Source (Wikimedia)
No undefined behavior [...], including memory safety and the absence of data races.
Stricter type system for further reduction of logic errors.
A clear distinction between safe and `unsafe` code.
Featureful language [...]
Extensive freestanding standard library [...]
Integrated out of the box tooling [...]
Overall, Rust is a language that has successfully leveraged decades of experience from system programming languages as well as functional ones [...] (source)
Rust is an intriguing language. It closely resembles C++ in many ways, hitting all the right notes [...]. [...] it also has the potential to solve some of the most vexing issues that plague C++ projects [...]. (source)
Rust provides memory safety guarantees [...] and runtime checks to ensure that memory accesses are valid. This safety is achieved while providing equivalent performance to C and C++. (source)
For developers, Rust offers the performance of older languages like C++ with a heavier focus on code safety. Today, there are hundreds of developers at Facebook writing millions of lines of Rust code. (source)
The Rust Foundation is an independent non-profit organization to steward the Rust programming language and ecosystem, with a unique focus on supporting the set of maintainers that govern and develop the project. (source)
Image by
Francis C. Franklin / CC-BY-SA-3.0
Source (Wikimedia)
#include <iostream>
#include <stdlib.h>
#include <string.h>
using namespace std;
char *getData() {
char *buffer = (char*)malloc(1024);
strcpy(buffer, "Hi!\n"); // read data from somewhere
return buffer;
}
void getAndProcessData() {
char *buffer = getData();
cout << buffer;
}
int main() {
getAndProcessData();
}See also CWE-401 Vulnerability
Try online (repl.it)
This is not best-practice C++ code, just for demo purposes. Use types like vector or string, RAII-techniques like shared pointers, etc. instead.
#include <iostream>
#include <string.h>
#include <stdlib.h>
using namespace std;
bool execSql(const char *stmt) { return false; }
void logError(const char *category, const char *details) {
cout << category << " (" << details << ')';
}
int main() {
char* stmt = (char*)malloc(100);
strcpy(stmt, "INSERT INTO ...");
bool errorHappened = false;
if (!execSql(stmt)) {
errorHappened = true;
free((void*)stmt);
}
// Do something else
if (errorHappened) {
logError("Error executing SQL", stmt);
}
}See also CWE-416 Vulnerability
using System;
class SqlConnection : IDisposable { ... }
...
OpenConnection();
bool errorHappened = false;
try {
ExecSql(Connection, "INSERT INTO ...");
// Continue working with DB
}
catch {
errorHappened = true;
Connection.Dispose();
}
// Do something else
if (errorHappened) {
LogError("Error executing SQL", Connection);
}Managed Language (C#)
Try online (repl.it)
#include <iostream>
using namespace std;
void printValue(const int *p) {
cout << *p << '\n'; // Print value on screen
delete p; // Cleanup by freeing p
}
int main() {
int* p = new int; // Allocate and init
*p = 42;
printValue(p); // Call method to print value
delete p; // Cleanup by freeing p
cout << "done.\n";
}
using System;
using System.Buffers;
class MainClass {
public static void Main(string[] args) {
var numbers = ArrayPool<int>.Shared.Rent(4);
try { DoSomethingWithNumbers(numbers); }
finally { ArrayPool<int>.Shared.Return(numbers); }
}
public static void DoSomethingWithNumbers(int[] numbers) {
Task.Run(async () => {
// Do something with Numbers
ArrayPool<int>.Shared.Return(numbers);
});
}
}
Managed Language (C#)
Ownership rules enforced by compiler
fn main() {
let numbers = vec![1, 1, 2, 3, 5, 8];
let other_numbers = numbers;
println!("{:?}", other_numbers);
}fn main() {
let numbers = vec![1, 1, 2, 3, 5, 8];
let other_numbers = numbers;
println!("{:?}", other_numbers);
println!("{:?}", numbers);
}numbers owns vector
Ownership change
Not allowed
Free vector ONCE
Try online (repl.it)
fn main() {
// Allocate array on heap
let numbers = vec![1, 2, 3, 5, 8];
println!("{:?}", numbers);
// Move ownership to other_numbers
let other_numbers = numbers;
println!("{:?}", other_numbers);
// Now we cannot access numbers anymore
// because value was moved.
// println!("{:?}", numbers); // -> does not COMPILE
// Make a (deep) copy -> no move of ownership
let cloned_numbers = other_numbers.clone();
println!("clone = {:?}, source = {:?}", cloned_numbers, other_numbers);
}Clone -> 2 owners
Try online (repl.it)
fn main() {
let numbers = vec![1, 2, 3, 5, 8];
consume(numbers); // Gives ownership to `consume`
let produced_numbers = produce(); // Takes ownership
println!("{:?}", produced_numbers);
// produced_numbers gets out of scope -> free memory
}
fn consume(numbers: Vec<i32>) {
let sum: i32 = numbers.iter().sum();
println!("The sum is {}", sum);
// numbers gets out of scope -> free memory
}
fn produce() -> Vec<i32> {
let mut numbers: Vec<i32> = Vec::new();
for i in 0..4 { numbers.push(i); }
numbers // Gives ownership to caller
}numbers owns vector
Try online (repl.it)
Ownership change
Free vector
numbers owns vector
Free vector
Ownership change
fn main() {
let mut numbers = vec![1, 2, 3, 5, 8];
println!("The sum is {}",
consume(&numbers)); // Passes reference, keeps ownership
println!("The sum is {}",
add_and_consume(&mut numbers)); // Mutable reference, keeps ownership
println!("{:?}", numbers);
}
fn consume(numbers: &Vec<i32>) -> i32 {
// numbers is read-only, cannot be mutated
//numbers.push(42); // -> does NOT COMPILE
let sum: i32 = numbers.iter().sum();
sum
}
fn add_and_consume(numbers: &mut Vec<i32>) -> i32 {
numbers.push(42);
consume(numbers)
}Try online (repl.it)
Borrows vector
Borrows mutable vec
Free vector
Is still owner
use futures::executor::block_on;
use futures_timer::Delay;
use std::time::Duration;
fn main() {
block_on(async_main());
}
async fn async_main() {
let mut numbers = vec![1, 2, 3, 5, 8];
let sum_future = sum(&numbers);
add(&mut numbers);
println!("The sum is {}", sum_future.await);
}
fn add(numbers: &mut Vec<i32>) {
numbers.push(42);
}
async fn sum(numbers: &Vec<i32>) -> i32 {
let iter = numbers.iter();
Delay::new(Duration::from_secs(2)).await;
iter.sum()
}
using System;
using static System.Console;
using System.Collections.Generic;
using System.Threading.Tasks;
class MainClass {
public static void Main (string[] args) {
var numbers = new List<int> {1,2,3,5,8};
var sumTask = SumAsync(numbers);
numbers.Add(13);
WriteLine($"The sum is {sumTask.Result}");
}
static async Task<int> SumAsync(
IEnumerable<int> numbers) {
var sum = 0;
foreach (var n in numbers) {
await Task.Delay(10);
sum += n;
}
return sum;
}
}Compile-time error
Runtime error
Rust
C#
use std::time::Duration;
use std::sync::mpsc;
use std::thread;
fn main() {
let (sender, receiver) = mpsc::channel::<i32>();
thread::spawn(move || {
for i in 0..5 {
sender.send(i).unwrap();
thread::sleep(Duration::from_millis(500));
}
});
loop {
match receiver.recv() {
Ok(result) => println!("Received: {}", result),
Err(_) => {
println!("Done!");
break;
}
};
}
}
Try online (repl.it)
Receiver ownership
Move sender owner
struct Point {
x: f32,
y: f32,
}
struct Rect {
left_upper: Point,
right_lower: Point,
}
struct Circle {
center: Point,
radius: f32,
}fn main() {
// Create an instance of Point on the stack
let p1 = Point { x: 20f32, y: 20f32 };
// Create another Point and take some values from p1
let p2 = Point { x: 21f32, ..p1 };
// By default, variables are immutable.
// Let's create a mutable rect.
let mut r = Rect { left_upper: p1, right_lower: p2 };
r.right_lower.y = 40f32;
// Create a Circle on the heap
let mut c = Box::new(
Circle { center: Point { x: 10f32, y: 10f32 },
radius: 42f32 });
c.radius = 10f32;
}let mut r = Rect { ... };
r.enlarge(2.0f32);
println!("The area is {}",
c.area());
impl Rect {
fn width(&self) -> f32 {
self.right_lower.x - self.left_upper.x
}
fn height(&self) -> f32 {
self.right_lower.y - self.left_upper.y
}
fn enlarge(&mut self, factor: f32) {
self.right_lower.x = self.left_upper.x + self.width() * factor;
self.right_lower.y = self.left_upper.y + self.height() * factor;
}
fn area(&self) -> f32 {
self.width() * self.height()
}
}
impl Circle {
fn unit_circle() -> Self {
Circle { center: Point { x: 0f32, y: 0f32 }, radius: 1f32 }
}
fn area(&self) -> f32 {
consts::PI * self.radius * self.radius
}
}
let u = Circle::unit_circle();
println!("The area is {}",
u.area());
trait Area {
fn area(&self) -> f32;
}
impl Area for Rect {
fn area(&self) -> f32 {
self.width() * self.height()
}
}
impl Area for Circle {
fn area(&self) -> f32 {
consts::PI * self.radius * self.radius
}
}let mut r = Rect {...};
let mut c = Box::new(Circle {...});
...
let shape_with_area: &dyn Area = &r;
println!("The area is {}",
shape_with_area.area());
let mut shapes = Vec::<&dyn Area>::new();
shapes.push(&r);
shapes.push(c.as_ref());
for s in shapes {
println!("The area is {}", s.area());
}
impl Add<Point> for Point {
type Output = Self;
fn add(self, other: Point) -> Self {
Point { x: self.x + other.x, y: self.y + other.y }
}
}
impl Display for Point {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{{ x: {}, y: {} }}", self.x, self.y)
}
}let origin = Point { x: 20f32, y: 20f32 };
println!("{}", origin);
let other = origin + Point { x: 20f32, y: 20f32 };
println!("{}", other);#[derive(Debug)]
struct Point {
x: f32,
y: f32,
}let points = vec![
Point { x: 20f32, y: 20f32 },
Point { x: 21f32, y: 21f32 }
];
println!("{:?}", &points);macro_rules! return_status {
($status:literal) => {
{
println!("Status: {}\n", $status);
return;
}
};
}
macro_rules! get_param {
($status:literal) => {
{
match std::env::args().filter(|a| a.starts_with($status)).nth(0) {
Some(p) => match p[$status.len()..].parse() {
Ok(p) => p,
Err(_) => return_status!(400)
},
None => return_status!(400)
}
}
};
}fn main() {
println!("Content-Type: text/html");
// Get parameters from args (=requested date)
let year = get_param!("year=");
let month = get_param!("month=");
let day = get_param!("day=");
let str = match get(URL) {
Ok(d) => d,
Err(_) => return_status!(404)
};
//...
}struct Raise {
current_pot: f32,
raise: f32,
}
enum PokerMove {
Fold,
Call(f32), // Note that enums can have values
Raise(Raise)
}let poker_move = PokerMove::Raise(Raise { current_pot: 42f32, raise: 2f32 });
match poker_move {
PokerMove::Call(x) => println!("Player calls at pot size {}", x),
PokerMove::Raise(x) => println!("Player raises, pot size now {}", x.current_pot + x.raise),
_ => println!("Player folds"),
}let moves = vec![
PokerMove::Fold,
PokerMove::Call(2f32),
PokerMove::Call(2f32),
PokerMove::Raise(Raise { current_pot: 42f32, raise: 2f32 })
];
let poker_move: Option<&PokerMove> = moves.iter().nth(4);
match poker_move {
Some(_) => println!("Found a move"),
None => println!("This move does not exist"),
};let contents: Result<String, std::io::Error> = fs::read_to_string("main.rs");
match contents {
Result::Ok(data) => println!("{}", data),
Result::Err(err) => println!("Error: {}", err),
};
pub fn try_from<T: AsRef<str>>(location: T) -> Result<BoardIndex, &'static str> {
let location = location.as_ref().as_bytes(); // Note shadowing
...
}
fn main() {
let mut _some_value = 42i32;
{
let _some_value = _some_value; // Note shadowing by immutable version
// _some_value = 43i32; --> does not compile
}
_some_value = 43i32; // --> This is fine
}fn read_data() -> Result<String, io::Error> {
let mut f = File::open("data.txt")?;
let mut s = String::new();
f.read_to_string(&mut s)?;
Ok(s)
}/// Runs a given closure in parallel on all available CPUs...
///
/// # Examples
///
/// ```
/// ...
/// let result = mth_calc::run_on_all_cpus::<f32>(|| {
/// ...
/// }, 0f32, |x, y| { x + y });
///
/// let avg = result.0 / (result.1 * 10) as f32;
/// # if avg < 0f32 || avg >= 1f32 {
/// # panic!();
/// # }
/// println!("The average random number was {}", avg);
/// ```
pub fn run_on_all_cpus<T: Send + 'static>(body: fn() -> T, initial: T,
agg: fn(T, T) -> T) -> (T, u32) {
...
}
#[cfg(test)]
mod tests {
...
#[test]
fn it_calculates_result_correctly() {
...
}
}fn main() {
let mut my_vec = Vec::new(); // Compiler does not know type of vector yet
my_vec.push(42i32); // Now type has become clear, it is i32
// my_vec.push("42"); -> does not compile because of type mismatch
println!("{:?}", my_vec);
}Thank you for attending!
Rainer Stropek | @rstropek