Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
SlideShare a Scribd company logo
Rust: абстракции и безопасность, 
совершенно бесплатно 
Владимир Матвеев 
vmatveev@qubell.com 
1 / 70
Место 
Близко к железу, но большой простор для ошибок: 
С 
С++ 
Высокоуровневые, безопасные, но дают меньше контроля: 
Java 
Haskell 
Python 
Ruby 
JS 
Go 
... 
2 / 70
Классы ошибок 
В C++: 
Висящие указатели 
Доступ за границами массивов 
Утечки памяти 
Переполнение буфера 
Use-after-free 
Гонки данных 
Iterator invalidation 
В Java: 
NullPointerException 
ConcurrentModificationException 
Утечки памяти (!) 
3 / 70
Выводы 
Просто «Best practices» недостаточно 
Безопасность с помощью идиом/гайдов не обеспечить 
Компилятор должен сам отклонять небезопасные 
программы 
4 / 70
Решение 
Rust обеспечивает безопасность работы с памятью с 
помощью мощного статического анализа 
Нет явного управления памятью, компилятор отслеживает 
аллокации и деаллокации 
Если программа компилируется, то она работает с 
памятью безопасно 
Zero-cost abstractions, как в С++ 
Вывод типов сильно помогает как при написании, так и 
при чтении 
5 / 70
История 
Появился как личный проект 
С 2009 спонсируется Mozilla Research 
Первый анонс в 2010 году 
В 2011 компилирует себя 
В 2012 выходит версия 0.1 
... 
6 / 70
Будущее 
Начало 2015 - релиз первой стабильной версии 
Стабилизация языка и API 
Центральный репозиторий пакетов 
Множество отложенных фич 
Типы высшего порядка 
Ещё более продвинутые макросы 
Вычисления при компиляции 
... 
7 / 70
Rust 
компилируемый (LLVM) 
быстрый 
безопасный 
со статической типизацией 
с выводом типов 
с бесплатными абстракциями 
с функциональными элементами 
минимальный рантайм (либо его отсутствие) 
... 
8 / 70
Кроссплатформенность 
LLVM => множество платформ 
Официально поддерживаются: 
Linux 
Mac OS X 
Win32/Win64 
Также работает под Android и iOS 
9 / 70
Типы данных 
10 / 70
Встроенные типы 
C фиксированным размером: 
i8, i16, i32, i64, u8, u16, u32, u64, f32, f64 
С платформозависимым размером: int, uint 
Массивы и строки: [u8, ..16], [i16], str 
Кортежи: (), (u16,), (f64, f64) 
Ссылки: &mut T, &T 
«Сырые» указатели: *mut T, *const T 
Указатели на функции: 
fn(u32, u32), extern "C" fn(u16) -> u16 
Замыкания: |int, int| -> int, proc(f64) -> u64 
11 / 70
Срезы 
let array: [u8, ..16] = [0, ..16]; 
let slice: &[u8] = &array; 
println!("{}", slice.len()); // 16 
Строковые срезы (и вообще строки) всегда в UTF-8: 
let s: &str = "abcde"; 
let str_buf: String = s.into_string(); 
12 / 70
Структуры 
Обычные: 
struct Point { 
x: f64, 
y: f64 
} 
Tuple structs: 
struct Point(f64, f64); 
Newtypes: 
struct Distance(uint); 
13 / 70
Перечисления 
Как в C: 
enum Direction { 
North, West = 123, 
South = 324, East 
} 
Как в Haskell: 
enum Option<T> { 
Some(T), 
None 
} 
14 / 70
Перечисления 
С вариантами-структурами: 
enum Event { 
KeyPress { keycode: uint, modifiers: uint }, 
MouseMove { x: u32, y: u32 }, 
... 
} 
15 / 70
Основные элементы языка 
16 / 70
С-подобный синтаксис 
fn main() { 
for i in range(0, 10) { 
println!("Hello world!"); 
} 
} 
Всё — выражения 
let m = if x % 2 == 0 { "even" } else { "odd" }; 
Вывод типов 
fn take_int(x: int) { ... } 
let x = 10; 
take_int(x); // x is inferred as int 
17 / 70
Циклы 
let (mut x, mut y) = (random_int(), random_int()); 
loop { 
x += 3; 
if x < y { continue; } 
y -= 3; 
if x > y { break; } 
} 
let mut n = 0; 
while n < 100 { 
n += 1; 
if n % 5 == 2 { n += 13; } 
} 
18 / 70
Сопоставление с образцом 
switch как в C: 
let x: uint = 10; 
let name = match x { 
1 => "one", 
2 => "two", 
_ => "many" 
}; 
Деструктуризация как в Haskell: 
let mut f = File::open("/tmp/input"); 
match f.read_to_end() { 
Ok(content) => println!("{} bytes", content.len()), 
Err(e) => println!("Error: {}", e) 
} 
19 / 70
Сопоставление с образцом 
Для срезов: 
let x = [1, 2, 3, 4]; 
match x.as_slice() { 
[1, x, ..rest] => { 
println!("2nd: {}, all others: {}", x, rest); 
} 
_ => println!("Something else") 
} 
При объявлении переменных и параметров: 
fn sum_tuple((x, y): (int, int)) -> int { 
x + y 
} 
20 / 70
Сопоставление с образцом 
if let, while let из Swift: 
if let Some(r) = from_str::<int>("12345") { 
println!("String "12345" is {}", r); 
} 
while let Ok(token) = next_token() { 
println!("Next token: {}", token); 
} 
21 / 70
Функции 
fn multiply(left: uint, right: uint) -> uint { 
left + right 
} 
#[no_mangle] 
pub extern fn visible_from_c(arg: u32) -> u32 { 
arg + arg 
} 
22 / 70
Методы 
struct Counter { 
base: u64 
} 
impl Counter { 
fn new(base: u64) -> Counter { 
Counter { base: base } 
} 
fn next(&mut self) -> u64 { 
let t = self.base; 
self.base += 1; 
return t; 
} 
} 
let mut counter = Counter::new(10); 
println!("{} -> {} -> {}", 
counter.next(), counter.next(), counter.next()); 
23 / 70
Полиморфизм 
24 / 70
Дженерики 
Как шаблоны в C++: 
enum Option<T> { 
None, Some(T) 
} 
fn unwrap_or<T>(opt: Option<T>, default: T) -> T { 
match opt { 
Some(value) => value, 
None => default 
} 
} 
println!("{}", unwrap_or(Some(10), 20)); // 10 
println!("{}", unwrap_or(None, "abcde")); // abcde 
25 / 70
Дженерики 
enum Option<T> { 
None, Some(T) 
} 
impl<T> Option<T> { 
fn unwrap_or(self, default: T) -> T { 
match self { 
Some(value) => value, 
None => other 
} 
} 
} 
println!("{}", Some(10).unwrap_or(20)); // 10 
println!("{}", None.unwrap_or("abcde")); // abcde 
26 / 70
Трейты 
Ограничения на ти́ повые переменные: 
trait Display { 
fn display(&self) -> String; 
} 
impl Display for int { 
fn display(&self) -> String { self.to_string() } 
} 
impl Display for String { 
fn display(&self) -> String { self.clone() } 
} 
fn print_twice<T: Display>(value: &T) { 
let s = value.display(); 
println!("{} {}", value, value); 
} 
27 / 70
Трейты 
trait Add<RHS, Result> { fn add(&self, rhs: &RHS) -> Result; } 
trait Mul<RHS, Result> { fn mul(&self, rhs: &RHS) -> Result; } 
fn lin_map<T: Add<T, T>+Mul<T, T>>(a: T, b: T, x: T) -> T { 
a*x + b 
} 
// more readable 
fn lin_map<T>(a: T, b: T, x: T) -> T 
where T: Add<T, T> + Mul<T, T> { 
a*x + b 
} 
28 / 70
Трейты 
Ср. с классами типов в Haskell: 
class Display a where 
display :: a -> String 
class Add a rhs result where 
add :: a -> rhs -> result 
А также реализации для произвольных типов, множественная 
диспетчеризация, ассоциированные типы, etc. 
29 / 70
Trait objects 
fn print_slice<T: Show>(items: &[T]) { 
for item in items.iter() { 
println!("{} ", item); 
} 
} 
print_slice(&[1i, 2i, 3i]); // ok 
print_slice(&["a", "b"]); // ok 
print_slice(&[1i, 2i, "a"]); // compilation error :( 
30 / 70
Trait objects 
Трейты как интерфейсы: 
fn print_slice(items: &[&Show]) { 
for item in items.iter() { 
println!("{}", item); 
} 
} 
print_slice(&[&1i, &2i, &3i]); // ok 
print_slice(&[&"a", &"b"]); // ok 
print_slice(&[&1i, &2i, &"a"]); // ok! 
31 / 70
Трейты 
Zero-cost on-demand abstraction: 
Ограничения на дженерики — статический полиморфизм, 
мономорфизация, инлайнинг 
Trait objects — динамический полиморфизм, виртуальные 
таблицы, позднее связывание 
Cost is explicit — сразу видно, где именно появляется 
оверхед 
32 / 70
Владение данными 
33 / 70
Ownership and borrowing 
Владение и заимствование — ключевые концепции Rust 
С помощью статических проверок на их основе компилятор 
способен предотвратить огромное число ошибок управления 
ресурсами: use-after-free, double-free, iterator invalidation, data 
races 
Владение данными основывается на теории линейных типов 
(linear types). Авторы Rust вдохновлялись языками Clean и 
Cyclone; см. также unique_ptr в C++ 
34 / 70
Ownership 
{ 
int *x = malloc(sizeof(int)); 
// do stuff 
*x = 5; 
free(x); 
} 
35 / 70
Ownership 
{ 
int *x = malloc(sizeof(int)); 
// do stuff 
*x = 5; 
free(x); 
} 
{ 
let x = box 5; 
} 
36 / 70
Ownership 
fn add_one(mut num: Box<int>) { 
*num += 1; 
} 
let x = box 5i; 
add_one(x); 
println!("{}", x); // ! error: use of moved value: x 
Move-семантика в действии! 
37 / 70
Ownership 
fn add_one(mut num: Box<int>) -> Box<int> { 
*num += 1; 
num 
} 
let x = box 5i; 
let y = add_one(x); 
println!("{}", y); // 6 
38 / 70
Copy 
Некоторые типы реализуют трейт Copy; они автоматически 
копируются вместо перемещения: 
let x: int = 10; 
let y = x; 
println!("{}", x); 
39 / 70
RAII 
Владение данными + move semantics + деструкторы = 
безопасный RAII 
{ 
let mut f = File::open(&Path::new("/some/path")).unwrap(); 
// work with file ... 
} // f's destructor is called here 
// (unless it is moved somewhere else) 
Но move semantics подразумевает передачу права владения, 
что возможно далеко не всегда: 
let mut f = File::open(&Path::new("/some/path")).unwrap(); 
let buf = [0u8, ..128]; 
f.read(buf).unwrap(); 
println!("{}", buf); // ! use of moved value: buf 
40 / 70
Borrowing 
Владелец данных может предоставить к ним доступ с 
помощью ссылок: 
fn with_one(num: &int) -> int { 
*num + 1 
} 
let x = box 5i; 
println!("{}", with_one(&*x)); // 6 
41 / 70
Borrowing 
&T — разделяемые/иммутабельные (shared/immutable) 
&mut T — неразделяемые/мутабельные (exclusive/mutable) 
let x = 10i; 
let p1 = &x; 
let p2 = &x; // ok 
let mut x = 10i; 
let p1 = &mut x; 
let p2 = &x; // ! cannot borrow x as immutable because 
// ! it is also borrowed as mutable 
let mut x = 10i; 
let p1 = &mut x; 
let p2 = &mut x; // ! cannot borrow x as mutable 
// ! more than once at a time 
42 / 70
Borrowing and mutability 
«Эксклюзивность» мутабельных ссылок исключает очень 
большой класс ошибок вида use-after-free (и не только): 
let mut v: Vec<int> = vec![1, 2]; 
let e = &v[0]; 
v.push(3); // reallocates the vector, moving its contents 
// ! cannot borrow v as mutable because 
// ! it is also borrowed as immutable 
let mut num = box 5i; 
let e = &*num; 
num = box 6i; // ! cannot assign to num because it is borrowed 
let mut v = vec![1i, 2, 3]; 
for &e in v.iter() { 
println!("{}", e); 
if e == 2 { v.push(-42); } // ! cannot borrow v as mutable 
} 
43 / 70
Borrowing and mutability 
Отсутствие неожиданностей: 
fn do_stuff(data: &mut BigData, should_process: || -> bool) { 
assert!(data.is_safe()); 
if should_process() { 
unsafely_handle_data(data); 
} 
} 
44 / 70
Borrowing and mutability 
Наличие двух мутабельных ссылок позволяет реализовать 
transmute() (aka reinterpret_cast) в безопасном коде: 
fn my_transmute<T: Clone, U>(value: T, other: U) -> U { 
let mut x = Left(other); 
let y = match x { 
Left(ref mut y) => y, 
Right(_) => panic!() 
}; 
x = Right(value); 
y.clone() 
} 
45 / 70
Lifetimes 
«Наивное» заимствование может вызвать проблемы: 
1. создаётся ресурс X; 
2. на ресурс X берётся ссылка a; 
3. ресурс X уничтожается; 
4. к [уничтоженному] ресурсу X осуществляется доступ через 
ссылку a. 
Use after free, доступ к закрытому файлу, etc. 
Решение — статически обеспечить невозможность 4 перед 3. 
46 / 70
Lifetimes 
С каждой ссылкой ассоциирован параметр — время жизни 
того объекта, на который она указывает. Компилятор 
статически проверяет, что каждая ссылка всегда «живёт» 
меньше, чем исходный объект: 
fn make_ref<'a>() -> &'a int { 
let x = 10i; 
&x // ! x does not live long enough 
} 
fn first_and_last<'a>(slice: &'a [T]) -> (&'a T, &'a T) { 
(&slice[0], &slice[slice.len()-1]) 
} 
fn first_and_last(slice: &[T]) -> (&T, &T) { // identical 
(&slice[0], &slice[slice.len()-1]) 
} 
47 / 70
Lifetimes 
Lifetime-параметры можно ассоциировать с областями 
видимости: 
let x; 
{ 
let n = 5i; 
x = &n; // ! n does not live long enough 
} 
println!("{}", *x); 
48 / 70
Lifetimes 
Lifetime-параметры «заражают» типы: 
struct AnIntReference<'a> { 
r: &'a int 
} 
enum MaybeOwned<'a> { 
Owned(String), 
Slice(&'a str) 
} 
49 / 70
Lifetimes 
Специальный идентификатор 'static обозначает время жизни 
всей программы: 
static ANSWER: int = 42; 
fn print_static_int_only(r: &'static int) { // ' 
println!("{}", *r); 
} 
fn main() { 
print_static_int_only(&ANSWER); // ok 
let r = 21; 
print_static_int_only(&r); // ! r does not live long enough 
} 
const MESSAGE: &'static str = "Hello world!"; 
50 / 70
Shared ownership 
В рамках одного потока — подсчёт ссылок: 
use std::rc::Rc; 
{ 
let r = Rc::new(vec![1, 2, 3]); 
let r2 = r.clone(); 
println!("{}", *r); 
println!("{}", *r2); 
} // both references go out of scope, Vec is destroyed 
51 / 70
Многопоточность 
52 / 70
Потоки 
Создаются функцией spawn(): 
spawn(move || { // unboxed closure 
println!("Hello from other thread!"); 
}); 
Потоки — это потоки ОС. 
Система типов гарантирует, что замыкание не захватит 
«опасные» переменные. 
53 / 70
Каналы 
Общение между потоками происходит через каналы: 
let (tx, rx) = channel(); 
spawn(move || { 
tx.send(4u + 6); 
tx.send(5u + 7); 
}); 
println!("{}, {}", rx.recv(), rx.recv()); 
54 / 70
Shared state 
Данные «без ссылок внутри» разделяемые с помощью Arc: 
use std::sync::Arc; 
let data = Arc::new(vec![1u, 2, 3]); 
let for_thread = data.clone(); 
spawn(move || { 
println!("From spawned thread: {}", *for_thread); 
}); 
println!("From main thread: {}", *data); 
55 / 70
Mutable shared state 
За счёт системы типов использование синхронизации 
обязательно. Таким образом, исключаются гонки данных 
(data races): 
use std::sync::{Arc, Mutex}; 
let data = Arc::new(Mutex::new(vec![1u, 2, 3])); 
let for_thread = data.clone(); 
spawn(move || { 
let mut guard = for_thread.lock(); 
guard.push(4); 
println!("{}", *guard); 
}); 
let mut guard = data.lock(); 
guard.push(5); 
println!("{}", *guard); 
56 / 70
Unsafe 
57 / 70
Что это такое 
unsafe-блоки и unsafe-функции: 
unsafe fn from_raw_buf<'a, T>(p: &'a *const T, 
n: uint) -> &'a [u8] { 
std::mem::transmute(std::raw::Slice { 
data: *p, 
len: n 
}) 
} 
fn fill_buffer<R: Reader>(r: R, size: uint) -> Vec<u8> { 
let v = Vec::with_capacity(size); 
unsafe { v.set_len(size) }; 
r.read(v.as_mut_slice()).unwrap(); 
v 
} 
58 / 70
Unsafe-операции 
разыменование «сырого» указателя: 
let n = 10i; 
let p = &n as *const int; 
println!("{}", unsafe { *p }); 
чтение/запись статической мутабельной переменной: 
static mut COUNTER: u32 = 10; 
unsafe { COUNTER += 32 }; 
println!("{}", unsafe { COUNTER }); 
вызов unsafe-функции: 
extern { fn perror(); } 
unsafe { perror(); } 
59 / 70
Когда это нужно 
ещё больше производительности 
абстракции 
взаимодействие с внешними библиотеками 
Другими словами — очень редко! 
60 / 70
Инфраструктура 
61 / 70
Встроенные модульные тесты 
#[test] 
fn test_something() { 
assert_eq!(true, true); 
} 
#[bench] 
fn test_perf(b: &mut Bencher) { 
b.iter(|| { 
do_something(); 
}); 
} 
62 / 70
Единица компиляции — crate 
pub mod a { 
mod b { 
// ... 
} 
pub mod c { 
// ... 
} 
} 
mod d { 
// ... 
} 
На выходе — готовый бинарник (библиотека или executable) 
63 / 70
Менеджер сборки — Cargo 
разработан Yehuda Katz — автором Bundler 
сборка и управление проектом: 
отслеживание зависимостей 
компиляция зависимостей, как на Rust, так и на C 
компиляция проекта 
запуск тестов, модульных и интеграционных 
генерация пакетов и их деплой в репозиторий 
reproducible builds 
64 / 70
crates.io — центральный репозиторий 
Открылся совсем недавно 
Предназначен, в основном, для стабильных релизов 
400 пакетов спустя полторы недели 
Ядро будущей экосистемы 
65 / 70
Проекты на Rust 
66 / 70
Servo — https://github.com/servo/ 
исследовательский браузерный движок 
активно развивается, уже проходит какие-то тесты 
~100000 строк 
rustc — https://github.com/rust-lang/rust 
сам компилятор Rust 
самый старый крупный проект 
~400000 строк 
Cargo — https://github.com/rust-lang/cargo 
менеджер сборки 
один из наиболее новых проектов, idiomatic style 
~30000 строк 
67 / 70
Piston — https://github.com/PistonDevelopers 
коллекция проектов, связанных с разработкой игр 
байндинги к OpenGL и другим графическим (и не 
только) библиотекам 
игровой движок 
GUI-библиотека 
Zinc — https://zinc.rs 
ARM-стек 
эффективный и безопасный фреймворк для RTOS- 
систем 
~17000 строк 
Iron — https://ironframework.org/ 
наиболее популярный веб-фреймворк (есть и другие!) 
middleware-based 
вместе с HTTP-библиотекой Hyper ~8000 строк 
68 / 70
Ссылки 
69 / 70
Общее 
http://www.rust-lang.org/ 
https://github.com/rust-lang 
http://reddit.com/r/rust 
http://discuss.rust-lang.org/ 
https://crates.io/ 
irc.mozilla.org - #rust, #rust-internals, #cargo, #servo, ... 
https://groups.google.com/forum/#!forum/rust-russian 
http://blog.rust-lang.org/ 
http://habrahabr.ru/post/237199/ 
http://habrahabr.ru/post/243315/ 
Документация и туториалы 
Stackoverflow по тегу [rust] 
http://doc.rust-lang.org/guide.html 
https://github.com/rust-lang/rust/wiki/Docs 
https://rustbyexample.com/ 
70 / 70

More Related Content

Rust: абстракции и безопасность, совершенно бесплатно

  • 1. Rust: абстракции и безопасность, совершенно бесплатно Владимир Матвеев vmatveev@qubell.com 1 / 70
  • 2. Место Близко к железу, но большой простор для ошибок: С С++ Высокоуровневые, безопасные, но дают меньше контроля: Java Haskell Python Ruby JS Go ... 2 / 70
  • 3. Классы ошибок В C++: Висящие указатели Доступ за границами массивов Утечки памяти Переполнение буфера Use-after-free Гонки данных Iterator invalidation В Java: NullPointerException ConcurrentModificationException Утечки памяти (!) 3 / 70
  • 4. Выводы Просто «Best practices» недостаточно Безопасность с помощью идиом/гайдов не обеспечить Компилятор должен сам отклонять небезопасные программы 4 / 70
  • 5. Решение Rust обеспечивает безопасность работы с памятью с помощью мощного статического анализа Нет явного управления памятью, компилятор отслеживает аллокации и деаллокации Если программа компилируется, то она работает с памятью безопасно Zero-cost abstractions, как в С++ Вывод типов сильно помогает как при написании, так и при чтении 5 / 70
  • 6. История Появился как личный проект С 2009 спонсируется Mozilla Research Первый анонс в 2010 году В 2011 компилирует себя В 2012 выходит версия 0.1 ... 6 / 70
  • 7. Будущее Начало 2015 - релиз первой стабильной версии Стабилизация языка и API Центральный репозиторий пакетов Множество отложенных фич Типы высшего порядка Ещё более продвинутые макросы Вычисления при компиляции ... 7 / 70
  • 8. Rust компилируемый (LLVM) быстрый безопасный со статической типизацией с выводом типов с бесплатными абстракциями с функциональными элементами минимальный рантайм (либо его отсутствие) ... 8 / 70
  • 9. Кроссплатформенность LLVM => множество платформ Официально поддерживаются: Linux Mac OS X Win32/Win64 Также работает под Android и iOS 9 / 70
  • 11. Встроенные типы C фиксированным размером: i8, i16, i32, i64, u8, u16, u32, u64, f32, f64 С платформозависимым размером: int, uint Массивы и строки: [u8, ..16], [i16], str Кортежи: (), (u16,), (f64, f64) Ссылки: &mut T, &T «Сырые» указатели: *mut T, *const T Указатели на функции: fn(u32, u32), extern "C" fn(u16) -> u16 Замыкания: |int, int| -> int, proc(f64) -> u64 11 / 70
  • 12. Срезы let array: [u8, ..16] = [0, ..16]; let slice: &[u8] = &array; println!("{}", slice.len()); // 16 Строковые срезы (и вообще строки) всегда в UTF-8: let s: &str = "abcde"; let str_buf: String = s.into_string(); 12 / 70
  • 13. Структуры Обычные: struct Point { x: f64, y: f64 } Tuple structs: struct Point(f64, f64); Newtypes: struct Distance(uint); 13 / 70
  • 14. Перечисления Как в C: enum Direction { North, West = 123, South = 324, East } Как в Haskell: enum Option<T> { Some(T), None } 14 / 70
  • 15. Перечисления С вариантами-структурами: enum Event { KeyPress { keycode: uint, modifiers: uint }, MouseMove { x: u32, y: u32 }, ... } 15 / 70
  • 17. С-подобный синтаксис fn main() { for i in range(0, 10) { println!("Hello world!"); } } Всё — выражения let m = if x % 2 == 0 { "even" } else { "odd" }; Вывод типов fn take_int(x: int) { ... } let x = 10; take_int(x); // x is inferred as int 17 / 70
  • 18. Циклы let (mut x, mut y) = (random_int(), random_int()); loop { x += 3; if x < y { continue; } y -= 3; if x > y { break; } } let mut n = 0; while n < 100 { n += 1; if n % 5 == 2 { n += 13; } } 18 / 70
  • 19. Сопоставление с образцом switch как в C: let x: uint = 10; let name = match x { 1 => "one", 2 => "two", _ => "many" }; Деструктуризация как в Haskell: let mut f = File::open("/tmp/input"); match f.read_to_end() { Ok(content) => println!("{} bytes", content.len()), Err(e) => println!("Error: {}", e) } 19 / 70
  • 20. Сопоставление с образцом Для срезов: let x = [1, 2, 3, 4]; match x.as_slice() { [1, x, ..rest] => { println!("2nd: {}, all others: {}", x, rest); } _ => println!("Something else") } При объявлении переменных и параметров: fn sum_tuple((x, y): (int, int)) -> int { x + y } 20 / 70
  • 21. Сопоставление с образцом if let, while let из Swift: if let Some(r) = from_str::<int>("12345") { println!("String "12345" is {}", r); } while let Ok(token) = next_token() { println!("Next token: {}", token); } 21 / 70
  • 22. Функции fn multiply(left: uint, right: uint) -> uint { left + right } #[no_mangle] pub extern fn visible_from_c(arg: u32) -> u32 { arg + arg } 22 / 70
  • 23. Методы struct Counter { base: u64 } impl Counter { fn new(base: u64) -> Counter { Counter { base: base } } fn next(&mut self) -> u64 { let t = self.base; self.base += 1; return t; } } let mut counter = Counter::new(10); println!("{} -> {} -> {}", counter.next(), counter.next(), counter.next()); 23 / 70
  • 25. Дженерики Как шаблоны в C++: enum Option<T> { None, Some(T) } fn unwrap_or<T>(opt: Option<T>, default: T) -> T { match opt { Some(value) => value, None => default } } println!("{}", unwrap_or(Some(10), 20)); // 10 println!("{}", unwrap_or(None, "abcde")); // abcde 25 / 70
  • 26. Дженерики enum Option<T> { None, Some(T) } impl<T> Option<T> { fn unwrap_or(self, default: T) -> T { match self { Some(value) => value, None => other } } } println!("{}", Some(10).unwrap_or(20)); // 10 println!("{}", None.unwrap_or("abcde")); // abcde 26 / 70
  • 27. Трейты Ограничения на ти́ повые переменные: trait Display { fn display(&self) -> String; } impl Display for int { fn display(&self) -> String { self.to_string() } } impl Display for String { fn display(&self) -> String { self.clone() } } fn print_twice<T: Display>(value: &T) { let s = value.display(); println!("{} {}", value, value); } 27 / 70
  • 28. Трейты trait Add<RHS, Result> { fn add(&self, rhs: &RHS) -> Result; } trait Mul<RHS, Result> { fn mul(&self, rhs: &RHS) -> Result; } fn lin_map<T: Add<T, T>+Mul<T, T>>(a: T, b: T, x: T) -> T { a*x + b } // more readable fn lin_map<T>(a: T, b: T, x: T) -> T where T: Add<T, T> + Mul<T, T> { a*x + b } 28 / 70
  • 29. Трейты Ср. с классами типов в Haskell: class Display a where display :: a -> String class Add a rhs result where add :: a -> rhs -> result А также реализации для произвольных типов, множественная диспетчеризация, ассоциированные типы, etc. 29 / 70
  • 30. Trait objects fn print_slice<T: Show>(items: &[T]) { for item in items.iter() { println!("{} ", item); } } print_slice(&[1i, 2i, 3i]); // ok print_slice(&["a", "b"]); // ok print_slice(&[1i, 2i, "a"]); // compilation error :( 30 / 70
  • 31. Trait objects Трейты как интерфейсы: fn print_slice(items: &[&Show]) { for item in items.iter() { println!("{}", item); } } print_slice(&[&1i, &2i, &3i]); // ok print_slice(&[&"a", &"b"]); // ok print_slice(&[&1i, &2i, &"a"]); // ok! 31 / 70
  • 32. Трейты Zero-cost on-demand abstraction: Ограничения на дженерики — статический полиморфизм, мономорфизация, инлайнинг Trait objects — динамический полиморфизм, виртуальные таблицы, позднее связывание Cost is explicit — сразу видно, где именно появляется оверхед 32 / 70
  • 34. Ownership and borrowing Владение и заимствование — ключевые концепции Rust С помощью статических проверок на их основе компилятор способен предотвратить огромное число ошибок управления ресурсами: use-after-free, double-free, iterator invalidation, data races Владение данными основывается на теории линейных типов (linear types). Авторы Rust вдохновлялись языками Clean и Cyclone; см. также unique_ptr в C++ 34 / 70
  • 35. Ownership { int *x = malloc(sizeof(int)); // do stuff *x = 5; free(x); } 35 / 70
  • 36. Ownership { int *x = malloc(sizeof(int)); // do stuff *x = 5; free(x); } { let x = box 5; } 36 / 70
  • 37. Ownership fn add_one(mut num: Box<int>) { *num += 1; } let x = box 5i; add_one(x); println!("{}", x); // ! error: use of moved value: x Move-семантика в действии! 37 / 70
  • 38. Ownership fn add_one(mut num: Box<int>) -> Box<int> { *num += 1; num } let x = box 5i; let y = add_one(x); println!("{}", y); // 6 38 / 70
  • 39. Copy Некоторые типы реализуют трейт Copy; они автоматически копируются вместо перемещения: let x: int = 10; let y = x; println!("{}", x); 39 / 70
  • 40. RAII Владение данными + move semantics + деструкторы = безопасный RAII { let mut f = File::open(&Path::new("/some/path")).unwrap(); // work with file ... } // f's destructor is called here // (unless it is moved somewhere else) Но move semantics подразумевает передачу права владения, что возможно далеко не всегда: let mut f = File::open(&Path::new("/some/path")).unwrap(); let buf = [0u8, ..128]; f.read(buf).unwrap(); println!("{}", buf); // ! use of moved value: buf 40 / 70
  • 41. Borrowing Владелец данных может предоставить к ним доступ с помощью ссылок: fn with_one(num: &int) -> int { *num + 1 } let x = box 5i; println!("{}", with_one(&*x)); // 6 41 / 70
  • 42. Borrowing &T — разделяемые/иммутабельные (shared/immutable) &mut T — неразделяемые/мутабельные (exclusive/mutable) let x = 10i; let p1 = &x; let p2 = &x; // ok let mut x = 10i; let p1 = &mut x; let p2 = &x; // ! cannot borrow x as immutable because // ! it is also borrowed as mutable let mut x = 10i; let p1 = &mut x; let p2 = &mut x; // ! cannot borrow x as mutable // ! more than once at a time 42 / 70
  • 43. Borrowing and mutability «Эксклюзивность» мутабельных ссылок исключает очень большой класс ошибок вида use-after-free (и не только): let mut v: Vec<int> = vec![1, 2]; let e = &v[0]; v.push(3); // reallocates the vector, moving its contents // ! cannot borrow v as mutable because // ! it is also borrowed as immutable let mut num = box 5i; let e = &*num; num = box 6i; // ! cannot assign to num because it is borrowed let mut v = vec![1i, 2, 3]; for &e in v.iter() { println!("{}", e); if e == 2 { v.push(-42); } // ! cannot borrow v as mutable } 43 / 70
  • 44. Borrowing and mutability Отсутствие неожиданностей: fn do_stuff(data: &mut BigData, should_process: || -> bool) { assert!(data.is_safe()); if should_process() { unsafely_handle_data(data); } } 44 / 70
  • 45. Borrowing and mutability Наличие двух мутабельных ссылок позволяет реализовать transmute() (aka reinterpret_cast) в безопасном коде: fn my_transmute<T: Clone, U>(value: T, other: U) -> U { let mut x = Left(other); let y = match x { Left(ref mut y) => y, Right(_) => panic!() }; x = Right(value); y.clone() } 45 / 70
  • 46. Lifetimes «Наивное» заимствование может вызвать проблемы: 1. создаётся ресурс X; 2. на ресурс X берётся ссылка a; 3. ресурс X уничтожается; 4. к [уничтоженному] ресурсу X осуществляется доступ через ссылку a. Use after free, доступ к закрытому файлу, etc. Решение — статически обеспечить невозможность 4 перед 3. 46 / 70
  • 47. Lifetimes С каждой ссылкой ассоциирован параметр — время жизни того объекта, на который она указывает. Компилятор статически проверяет, что каждая ссылка всегда «живёт» меньше, чем исходный объект: fn make_ref<'a>() -> &'a int { let x = 10i; &x // ! x does not live long enough } fn first_and_last<'a>(slice: &'a [T]) -> (&'a T, &'a T) { (&slice[0], &slice[slice.len()-1]) } fn first_and_last(slice: &[T]) -> (&T, &T) { // identical (&slice[0], &slice[slice.len()-1]) } 47 / 70
  • 48. Lifetimes Lifetime-параметры можно ассоциировать с областями видимости: let x; { let n = 5i; x = &n; // ! n does not live long enough } println!("{}", *x); 48 / 70
  • 49. Lifetimes Lifetime-параметры «заражают» типы: struct AnIntReference<'a> { r: &'a int } enum MaybeOwned<'a> { Owned(String), Slice(&'a str) } 49 / 70
  • 50. Lifetimes Специальный идентификатор 'static обозначает время жизни всей программы: static ANSWER: int = 42; fn print_static_int_only(r: &'static int) { // ' println!("{}", *r); } fn main() { print_static_int_only(&ANSWER); // ok let r = 21; print_static_int_only(&r); // ! r does not live long enough } const MESSAGE: &'static str = "Hello world!"; 50 / 70
  • 51. Shared ownership В рамках одного потока — подсчёт ссылок: use std::rc::Rc; { let r = Rc::new(vec![1, 2, 3]); let r2 = r.clone(); println!("{}", *r); println!("{}", *r2); } // both references go out of scope, Vec is destroyed 51 / 70
  • 53. Потоки Создаются функцией spawn(): spawn(move || { // unboxed closure println!("Hello from other thread!"); }); Потоки — это потоки ОС. Система типов гарантирует, что замыкание не захватит «опасные» переменные. 53 / 70
  • 54. Каналы Общение между потоками происходит через каналы: let (tx, rx) = channel(); spawn(move || { tx.send(4u + 6); tx.send(5u + 7); }); println!("{}, {}", rx.recv(), rx.recv()); 54 / 70
  • 55. Shared state Данные «без ссылок внутри» разделяемые с помощью Arc: use std::sync::Arc; let data = Arc::new(vec![1u, 2, 3]); let for_thread = data.clone(); spawn(move || { println!("From spawned thread: {}", *for_thread); }); println!("From main thread: {}", *data); 55 / 70
  • 56. Mutable shared state За счёт системы типов использование синхронизации обязательно. Таким образом, исключаются гонки данных (data races): use std::sync::{Arc, Mutex}; let data = Arc::new(Mutex::new(vec![1u, 2, 3])); let for_thread = data.clone(); spawn(move || { let mut guard = for_thread.lock(); guard.push(4); println!("{}", *guard); }); let mut guard = data.lock(); guard.push(5); println!("{}", *guard); 56 / 70
  • 58. Что это такое unsafe-блоки и unsafe-функции: unsafe fn from_raw_buf<'a, T>(p: &'a *const T, n: uint) -> &'a [u8] { std::mem::transmute(std::raw::Slice { data: *p, len: n }) } fn fill_buffer<R: Reader>(r: R, size: uint) -> Vec<u8> { let v = Vec::with_capacity(size); unsafe { v.set_len(size) }; r.read(v.as_mut_slice()).unwrap(); v } 58 / 70
  • 59. Unsafe-операции разыменование «сырого» указателя: let n = 10i; let p = &n as *const int; println!("{}", unsafe { *p }); чтение/запись статической мутабельной переменной: static mut COUNTER: u32 = 10; unsafe { COUNTER += 32 }; println!("{}", unsafe { COUNTER }); вызов unsafe-функции: extern { fn perror(); } unsafe { perror(); } 59 / 70
  • 60. Когда это нужно ещё больше производительности абстракции взаимодействие с внешними библиотеками Другими словами — очень редко! 60 / 70
  • 62. Встроенные модульные тесты #[test] fn test_something() { assert_eq!(true, true); } #[bench] fn test_perf(b: &mut Bencher) { b.iter(|| { do_something(); }); } 62 / 70
  • 63. Единица компиляции — crate pub mod a { mod b { // ... } pub mod c { // ... } } mod d { // ... } На выходе — готовый бинарник (библиотека или executable) 63 / 70
  • 64. Менеджер сборки — Cargo разработан Yehuda Katz — автором Bundler сборка и управление проектом: отслеживание зависимостей компиляция зависимостей, как на Rust, так и на C компиляция проекта запуск тестов, модульных и интеграционных генерация пакетов и их деплой в репозиторий reproducible builds 64 / 70
  • 65. crates.io — центральный репозиторий Открылся совсем недавно Предназначен, в основном, для стабильных релизов 400 пакетов спустя полторы недели Ядро будущей экосистемы 65 / 70
  • 67. Servo — https://github.com/servo/ исследовательский браузерный движок активно развивается, уже проходит какие-то тесты ~100000 строк rustc — https://github.com/rust-lang/rust сам компилятор Rust самый старый крупный проект ~400000 строк Cargo — https://github.com/rust-lang/cargo менеджер сборки один из наиболее новых проектов, idiomatic style ~30000 строк 67 / 70
  • 68. Piston — https://github.com/PistonDevelopers коллекция проектов, связанных с разработкой игр байндинги к OpenGL и другим графическим (и не только) библиотекам игровой движок GUI-библиотека Zinc — https://zinc.rs ARM-стек эффективный и безопасный фреймворк для RTOS- систем ~17000 строк Iron — https://ironframework.org/ наиболее популярный веб-фреймворк (есть и другие!) middleware-based вместе с HTTP-библиотекой Hyper ~8000 строк 68 / 70
  • 70. Общее http://www.rust-lang.org/ https://github.com/rust-lang http://reddit.com/r/rust http://discuss.rust-lang.org/ https://crates.io/ irc.mozilla.org - #rust, #rust-internals, #cargo, #servo, ... https://groups.google.com/forum/#!forum/rust-russian http://blog.rust-lang.org/ http://habrahabr.ru/post/237199/ http://habrahabr.ru/post/243315/ Документация и туториалы Stackoverflow по тегу [rust] http://doc.rust-lang.org/guide.html https://github.com/rust-lang/rust/wiki/Docs https://rustbyexample.com/ 70 / 70