Upload
open-it
View
333
Download
4
Embed Size (px)
DESCRIPTION
Владимир Матвеев: "Rust: абстракции и безопасность, совершенно бесплатно" (Обзор языка Rust: для чего он предназначен, его ключевые особенности, инфраструктура)
Citation preview
Место
Близко к железу, но большой простор для ошибок:
СС++
Высокоуровневые, безопасные, но дают меньше контроля:
JavaHaskellPythonRubyJSGo...
2 / 70
Классы ошибок
В C++:
Висящие указателиДоступ за границами массивовУтечки памятиПереполнение буфераUse-after-freeГонки данныхIterator invalidation
В Java:
NullPointerExceptionConcurrentModificationExceptionУтечки памяти (!)
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 => множество платформ
Официально поддерживаются:
LinuxMac OS XWin32/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)); // 10println!("{}", 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)); // 10println!("{}", 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 readablefn 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]); // okprint_slice(&["a", "b"]); // okprint_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]); // okprint_slice(&[&"a", &"b"]); // okprint_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, dataraces
Владение данными основывается на теории линейных типов(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.rsARM-стекэффективный и безопасный фреймворк для RTOS-систем~17000 строк
Iron — https://ironframework.org/наиболее популярный веб-фреймворк (есть и другие!)middleware-basedвместе с HTTP-библиотекой Hyper ~8000 строк
68 / 70
Ссылки
69 / 70
Общееhttp://www.rust-lang.org/https://github.com/rust-langhttp://reddit.com/r/rusthttp://discuss.rust-lang.org/https://crates.io/irc.mozilla.org - #rust, #rust-internals, #cargo, #servo, ...https://groups.google.com/forum/#!forum/rust-russianhttp://blog.rust-lang.org/
http://habrahabr.ru/post/237199/http://habrahabr.ru/post/243315/
Документация и туториалыStackoverflow по тегу [rust]http://doc.rust-lang.org/guide.htmlhttps://github.com/rust-lang/rust/wiki/Docshttps://rustbyexample.com/
70 / 70