50
Rust: modern, practical, safe, fast programming language Stepan Koltsov <[email protected]> Java Party, Kyiv

Rust: код может быть одновременно безопасным и быстрым, Степан Кольцов

  • Upload
    yandex

  • View
    807

  • Download
    0

Embed Size (px)

DESCRIPTION

Последние 15 лет между разработчиками на Java и на C++ ведётся спор о том, какой язык программирования хуже — Java или C++. Программы на C++ глючат, падают, и в них утекает память. Программы на Java тормозят и требуют слишком много памяти. Rust — новый язык программирования, разрабатываемый компанией Mozilla — решает проблемы Java и C++: программы, написанные на Rust, одновременно быстрые и безопасные. Rust является таким же низкоуровневым, close-to-metal языком программирования, как и C++, однако в язык встроены конструкции, позволяющие на этапе компиляции доказывать, что в программе не случится обращения к неинициализированной памяти (механизм borrowed pointers). Большая часть моего рассказа будет посвящена описанию этого механизма.

Citation preview

Page 1: Rust: код может быть одновременно безопасным и быстрым, Степан Кольцов

Rust: modern, practical, safe, fast programming language

Stepan Koltsov <[email protected]>

Java Party, Kyiv

Page 2: Rust: код может быть одновременно безопасным и быстрым, Степан Кольцов

Why Rust• Java

• Safe* • … at a price of CPU/mem overhead

• C++ • Very fast • Very low memory overhead • Unsafe

• Rust • As fast as C++*, safer then Java

Page 3: Rust: код может быть одновременно безопасным и быстрым, Степан Кольцов

Basic examples

Page 4: Rust: код может быть одновременно безопасным и быстрым, Степан Кольцов

fn main() { println!("hello world");}

Hello world

Page 5: Rust: код может быть одновременно безопасным и быстрым, Степан Кольцов

fn is_prime(n: uint) -> bool { range(2, n).all(|x| n % x != 0) // lambda}!

let primes: Vec<uint> = range(2u, 10000u) .filter(|&n| is_prime(n)).collect();

Functional

Page 6: Rust: код может быть одновременно безопасным и быстрым, Степан Кольцов

Data types

• primitives: bool, int, u32, f32, etc. • builtins: &[], &str • user-defined: struct, enum, trait • functions

Page 7: Rust: код может быть одновременно безопасным и быстрым, Степан Кольцов

struct SocketAddr { ipAddr: IpAddr, port: uint,}

struct

Page 8: Rust: код может быть одновременно безопасным и быстрым, Степан Кольцов

enum CacheStatus { Error(String), Cached(uint),}!// Haskell!data CacheStatus = Error String | Cached Int!// C++!struct CacheStatus { unsigned tag; union { std::string error; uint cached; }}!// Java!abstract class CacheStatus {}class Error { … }class Cached { … }

enum (tagged union)

Page 9: Rust: код может быть одновременно безопасным и быстрым, Степан Кольцов

fn create_server(conf: Conf) { … }!

fn main() { let conf = Conf { … } create_server(conf); // compile-time error: `conf` is moved println!("{}", conf);}

Everything is moved

Page 10: Rust: код может быть одновременно безопасным и быстрым, Степан Кольцов

fn create_server(conf: Conf) { … }!

fn main() { let conf = Conf { … } // pass a copy, keep the original create_server(conf.clone()); println!("{}", conf);}

Clone trait

Page 11: Rust: код может быть одновременно безопасным и быстрым, Степан Кольцов

Vec<T>; &[T]; String, &str

Java Java C++ Rust

ArrayList<T> IntBuffer std::vector<T> std::Vec<T>

.subList() .slice() std::array_view &[T]

StringBuffer std::string std::String

- std::string_view &str

Page 12: Rust: код может быть одновременно безопасным и быстрым, Степан Кольцов

// C++ std::array_view<T>; Rust &[T]struct Slice<T> { T* begin; T* end;}!

// C++ std::string_view; Rust: &strtype StrSlice = Slice<char>;

What is slice

Page 13: Rust: код может быть одновременно безопасным и быстрым, Степан Кольцов

// similar to std::vectorlet v1: Vec<uint> = vec!(10, 20, 30);!

// Slice, similar to std::array_viewlet v2: &[uint] = v1.as_slice();!

// another slicelet v3 = v2.slice_from(1);!

// prints [20, 30]println!("{}", v3.to_str());

Vec

Page 14: Rust: код может быть одновременно безопасным и быстрым, Степан Кольцов

fn sum<T : Num>(ns: &[T]) -> T { let sum = T::zero(); for n in ns { sum += n; } sum}

traits (type classes)

Page 15: Rust: код может быть одновременно безопасным и быстрым, Степан Кольцов

Pointers

• raw unsafe pointers *Foo • borrowed pointers &Foo • smart pointers

Page 16: Rust: код может быть одновременно безопасным и быстрым, Степан Кольцов

std::string get_url() { return "http://yandex.ru";}!string_view get_scheme_from_url(string_view url) { unsigned colon = url.find(':'); return url.substr(0, colon);}!int main() { auto scheme = get_scheme_from_url(get_url()); std::cout << scheme << "\n"; return 0;}

Motivating example

Page 17: Rust: код может быть одновременно безопасным и быстрым, Степан Кольцов
Page 18: Rust: код может быть одновременно безопасным и быстрым, Степан Кольцов

fn get_url() -> String { "http://yandex.ru".to_string()}!fn get_scheme_from_url<'s>(url: &'s str) -> &'s str { let colon = url.find_str("://").unwrap(); url.slice_to(colon)}!fn main() { // compile-time error let scheme2 = get_scheme_from_url(get_url().as_slice());! // works let url = get_url(); let scheme = get_scheme_from_url(url.as_slice()); println!("{}", scheme);}

Same in Rust

Page 19: Rust: код может быть одновременно безопасным и быстрым, Степан Кольцов
Page 20: Rust: код может быть одновременно безопасным и быстрым, Степан Кольцов

struct UrlParts<'a> { scheme: &'a str, host: &'a str, port: uint, path: &'a str,}!

fn parse_url<'a>(url: &'a str) -> UrlParts<'a>{ UrlParts { … }}

More examples

Page 21: Rust: код может быть одновременно безопасным и быстрым, Степан Кольцов

enum MaybeOwned<'a> { Slice(&'a str), Owned(String),}!

fn from_utf8_lossy<'a>(v: &'a [u8]) -> MaybeOwned<'a>{ … }

MaybeOwned

Page 22: Rust: код может быть одновременно безопасным и быстрым, Степан Кольцов

impl<'s> MaybeOwned { fn as_slice(&'s self) -> &'s str { match self { Owned(ref s) => s.as_slice(), Slice(s) => s, } }! fn into_string(self) -> String { match self { Owned(s) => s, Slice(s) => s.to_string(), } }}

enum pattern matching

Page 23: Rust: код может быть одновременно безопасным и быстрым, Степан Кольцов

struct Person { name: String,}!impl<'s> Person { fn get_name_or_default(&'s self) -> &'s str { if name.empty() { "unnamed" // &'static str } else { self.name.as_slice() } }}

Static Lifetime

Page 24: Rust: код может быть одновременно безопасным и быстрым, Степан Кольцов

fn common_prefix<'s>(a: &'s str, b: &'s str) -> &'s str{ … }!

fn foo<'a>(a: &'a str) -> &'a str { let b = "bb".to_string(); // lifetime of c is intersection let c = longest_str( a.as_slice(), b.as_slice()); return c; // error}

Lifetime intersection

Page 25: Rust: код может быть одновременно безопасным и быстрым, Степан Кольцов

void foo(vector<int>& xs) { typedef vector<int>::iterator iter; for (iter i = xs.begin(); i != xs.end(); ++i) { if (*i == 0) { // modCount in Java xs.push_back(1); } }}

Mutability: C++

Page 26: Rust: код может быть одновременно безопасным и быстрым, Степан Кольцов
Page 27: Rust: код может быть одновременно безопасным и быстрым, Степан Кольцов

fn foo(xs: &mut Vec<int>) { for p in xs.iter() { if *p == 0 { xs.push(1); } }}

Mutability: Rust

Page 28: Rust: код может быть одновременно безопасным и быстрым, Степан Кольцов
Page 29: Rust: код может быть одновременно безопасным и быстрым, Степан Кольцов

let mut a = 1;let b = &mut a;let c = &mut a;!tmp2.rs:4:18: 4:19 error: cannot borrow `a` as mutable more than once at a timetmp2.rs:4 let c = &mut a; ^tmp2.rs:3:18: 3:19 note: previous borrow of `a` occurs here; the mutable borrow prevents subsequent moves, borrows, or modification of `a` until the borrow endstmp2.rs:3 let b = &mut a; ^tmp2.rs:5:2: 5:2 note: previous borrow ends here

Cannot borrow as mutable twice

Page 30: Rust: код может быть одновременно безопасным и быстрым, Степан Кольцов

let mut a = 1;a = 2;let b = &a;a = 3;!!mut.rs:5:5: 5:6 error: cannot assign to `a` because it is borrowedmut.rs:5 a = 3; ^mut.rs:4:13: 4:15 note: borrow of `a` occurs heremut.rs:4 let b = &a; ^~

Cannot assign, because borrowed

Page 31: Rust: код может быть одновременно безопасным и быстрым, Степан Кольцов

Smart Pointers

C++ Rust

std::unique_ptr<T> Box<T>

std::shared_ptr<T> Rc<T> or Arc<T>

Page 32: Rust: код может быть одновременно безопасным и быстрым, Степан Кольцов

struct Foo { v: int,}!

let ptr = Rc::new(Foo { … });println!("{}", ptr.v);

User-defined pointers: Rc<T>

Page 33: Rust: код может быть одновременно безопасным и быстрым, Степан Кольцов

struct RcBox<T> { value: T, ref_count: uint,}!

pub struct Rc<T> { // unsafe pointer ptr: *mut RcBox<T>,}!

impl<T> Deref<T> for Rc<T> { fn deref(&'a self) -> &'a T { … }}

Rc impl

Page 34: Rust: код может быть одновременно безопасным и быстрым, Степан Кольцов

Threads

• tasks • channels • Arc • AtomicInt • Mutex

Page 35: Rust: код может быть одновременно безопасным и быстрым, Степан Кольцов

for i in range(0, 100) { // proc is a keyword // proc closure can be passed btw threads // and may be called no more than once task::spawn(proc() { println!("{}", i); });}

Tasks

Page 36: Rust: код может быть одновременно безопасным и быстрым, Степан Кольцов

let (sender, receiver) = channel();!for i in range(0, 100) { let sender_for_task = sender.clone(); task::spawn(proc() { // do something sender_for_task.send(i * i); });}!for i in range(0, 100) { let r = receiver.recv(); println!("{}", r);}

Channels

Page 37: Rust: код может быть одновременно безопасным и быстрым, Степан Кольцов

// similar to Rc<T> except// Arc uses atomic counter, not plain// data is immutable inside Arc// so Arc can be safely shared between threadslet conf = Arc::new(ServerConf { … });!for i in range(0, 100) { // must get a copy of Arc // to pass it to another thread let conf_c = conf.clone(); task::spawn(proc() { println!("{}", conf_c); });}

Arc<T>

Page 38: Rust: код может быть одновременно безопасным и быстрым, Степан Кольцов

Mutex<T>

• Mutex<T> = T + mutex • Safely share data between threads

Page 39: Rust: код может быть одновременно безопасным и быстрым, Степан Кольцов

fn do_smth(shared_data: Arc<Mutex<T>>) { // guard + smart pointer let ptr_and_lock = shared_data.lock();!

// do smth with guarded object ptr_and_lock.foo_bar();!

// ptr_and_lock destructor is called // and the end of the fn, // lock is released}

Mutex<T>

Page 40: Rust: код может быть одновременно безопасным и быстрым, Степан Кольцов

unsafe fn memset(mem: *mut u8, c: u8, len: uint){ for i in range(0, len) { *mem.offset(i as int) = c; }}!fn main() { let mut v: Vec<u8> = vec!(1, 2, 3); unsafe { memset(v.as_mut_ptr(), 10, v.len()); } println!("{}", v.to_str());}

unsafe

Page 41: Rust: код может быть одновременно безопасным и быстрым, Степан Кольцов

Program performance

0

25

50

75

100

Performance of compiled code

C++ Rust Java

Page 42: Rust: код может быть одновременно безопасным и быстрым, Степан Кольцов

Program safety

0

25

50

75

100

Performance of compiled code

C++ Rust Java

Page 43: Rust: код может быть одновременно безопасным и быстрым, Степан Кольцов

Development speed

0

25

50

75

100

Speed of development

C++ Rust Java

Page 44: Rust: код может быть одновременно безопасным и быстрым, Степан Кольцов

Problems

• compile-time metaprogramming • IDE • incremental compilation

Page 45: Rust: код может быть одновременно безопасным и быстрым, Степан Кольцов

trait Natural { fn zero() -> Self; fn next(self) -> Self;}!impl Natural for uint { fn zero() -> uint { 0 } fn next(self) -> uint { self + 1 }}!fn print_first_10_naturals<T : Natural + ToStr>() { let mut i: T = Natural::zero(); for _ in range(0, 10) { println!("{}", i.to_str()); i = i.next(); }}

Type classes (traits)

Page 46: Rust: код может быть одновременно безопасным и быстрым, Степан Кольцов

macro_rules! vec( ($($e:expr),*) => ({ let mut _temp = ::std::vec::Vec::new(); $(_temp.push($e);)* _temp }))!

let v = vec!(1, 2, if cond { 3 } else { 4 });vec!(struct); // fails at parse time

Macros

Page 47: Rust: код может быть одновременно безопасным и быстрым, Степан Кольцов

fn print_anything(xs: &[Box<ToStr>]) { for x in xs.iter() { println!("{}", x.to_str()); }}!

fn main() { let mut v: Vec<Box<ToStr>> = Vec::new(); v.push(box 1 as Box<ToStr>); v.push(box true as Box<ToStr>); print_anything(v.as_slice());}

Templates: dynamic dispatching

Page 48: Rust: код может быть одновременно безопасным и быстрым, Степан Кольцов

class FileInputStream: public InputStream { int fd;}!

// is actually!

struct FileInputStream { void* vtablePtr; int fd;}

C++/Java vtable

Page 49: Rust: код может быть одновременно безопасным и быстрым, Степан Кольцов

struct FileInputStream { fd: int}!impl InputStream for FileInputStream { … }!let r: &FileInputStream = …let s: &InputStream = r as &InputStream;!// is actually!struct InputStream_Ptr { data: &InputStream, vtable: …}!sizeof(r) == sizeof(void*)sizeof(s) == 2 * sizeof(void*)

Rust vtable

Page 50: Rust: код может быть одновременно безопасным и быстрым, Степан Кольцов

Fin

Stepan Koltsov <[email protected]>