SlideShare a Scribd company logo
Rust: modern, practical, safe, fast
programming language
Stepan Koltsov <nga@yandex-team.ru>
Java Party, Kyiv
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
Basic examples
fn main() {	
println!("hello world");	
}
Hello world
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
Data types
• primitives: bool, int, u32, f32, etc.
• builtins: &[], &str
• user-defined: struct, enum, trait
• functions
struct SocketAddr {	
ipAddr: IpAddr,	
port: uint,	
}
struct
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)
fn create_server(conf: Conf) { … }	
!
fn main() {	
let conf = Conf { … }	
create_server(conf);	
// compile-time error: `conf` is moved	
println!("{}", conf);	
}
Everything is moved
fn create_server(conf: Conf) { … }	
!
fn main() {	
let conf = Conf { … }	
// pass a copy, keep the original	
create_server(conf.clone());	
println!("{}", conf);	
}
Clone trait
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
// C++ std::array_view<T>; Rust &[T]	
struct Slice<T> {	
T* begin;	
T* end;	
}	
!
// C++ std::string_view; Rust: &str	
type StrSlice = Slice<char>;
What is slice
// similar to std::vector	
let v1: Vec<uint> = vec!(10, 20, 30);	
!
// Slice, similar to std::array_view	
let v2: &[uint] = v1.as_slice();	
!
// another slice	
let v3 = v2.slice_from(1);	
!
// prints [20, 30]	
println!("{}", v3.to_str());
Vec
fn sum<T : Num>(ns: &[T]) -> T {	
let sum = T::zero();	
for n in ns { sum += n; }	
sum	
}
traits (type classes)
Pointers
• raw unsafe pointers *Foo
• borrowed pointers &Foo
• smart pointers
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
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
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
enum MaybeOwned<'a> {	
Slice(&'a str),	
Owned(String),	
}	
!
fn from_utf8_lossy<'a>(v: &'a [u8])	
-> MaybeOwned<'a>	
{ … }
MaybeOwned
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
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
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
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++
Rust: код может быть одновременно безопасным и быстрым, Степан Кольцов
fn foo(xs: &mut Vec<int>) {	
for p in xs.iter() {	
if *p == 0 {	
xs.push(1);	
}	
}	
}
Mutability: Rust
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 time	
tmp2.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 ends	
tmp2.rs:3 let b = &mut a;	
^	
tmp2.rs:5:2: 5:2 note: previous borrow ends here
Cannot borrow as mutable twice
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 borrowed	
mut.rs:5 a = 3;	
^	
mut.rs:4:13: 4:15 note: borrow of `a` occurs here	
mut.rs:4 let b = &a;	
^~
Cannot assign, because borrowed
Smart Pointers
C++ Rust
std::unique_ptr<T> Box<T>
std::shared_ptr<T> Rc<T> or Arc<T>
struct Foo {	
v: int,	
}	
!
let ptr = Rc::new(Foo { … });	
println!("{}", ptr.v);
User-defined pointers: Rc<T>
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
Threads
• tasks
• channels
• Arc
• AtomicInt
• Mutex
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
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
// similar to Rc<T> except	
// Arc uses atomic counter, not plain	
// data is immutable inside Arc	
// so Arc can be safely shared between threads	
let 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>
Mutex<T>
• Mutex<T> = T + mutex
• Safely share data between threads
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>
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
Program performance
0
25
50
75
100
Performance of compiled code
C++ Rust Java
Program safety
0
25
50
75
100
Performance of compiled code
C++ Rust Java
Development speed
0
25
50
75
100
Speed of development
C++ Rust Java
Problems
• compile-time metaprogramming
• IDE
• incremental compilation
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)
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
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
class FileInputStream: public InputStream {	
int fd;	
}	
!
// is actually	
!
struct FileInputStream {	
void* vtablePtr;	
int fd;	
}	
C++/Java vtable
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
Fin
Stepan Koltsov <nga@yandex-team.ru>

More Related Content

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

  • 1. Rust: modern, practical, safe, fast programming language Stepan Koltsov <nga@yandex-team.ru> Java Party, Kyiv
  • 2. 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
  • 4. fn main() { println!("hello world"); } Hello world
  • 5. 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
  • 6. Data types • primitives: bool, int, u32, f32, etc. • builtins: &[], &str • user-defined: struct, enum, trait • functions
  • 7. struct SocketAddr { ipAddr: IpAddr, port: uint, } struct
  • 8. 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)
  • 9. fn create_server(conf: Conf) { … } ! fn main() { let conf = Conf { … } create_server(conf); // compile-time error: `conf` is moved println!("{}", conf); } Everything is moved
  • 10. fn create_server(conf: Conf) { … } ! fn main() { let conf = Conf { … } // pass a copy, keep the original create_server(conf.clone()); println!("{}", conf); } Clone trait
  • 11. 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
  • 12. // C++ std::array_view<T>; Rust &[T] struct Slice<T> { T* begin; T* end; } ! // C++ std::string_view; Rust: &str type StrSlice = Slice<char>; What is slice
  • 13. // similar to std::vector let v1: Vec<uint> = vec!(10, 20, 30); ! // Slice, similar to std::array_view let v2: &[uint] = v1.as_slice(); ! // another slice let v3 = v2.slice_from(1); ! // prints [20, 30] println!("{}", v3.to_str()); Vec
  • 14. fn sum<T : Num>(ns: &[T]) -> T { let sum = T::zero(); for n in ns { sum += n; } sum } traits (type classes)
  • 15. Pointers • raw unsafe pointers *Foo • borrowed pointers &Foo • smart pointers
  • 16. 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
  • 18. 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
  • 20. 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
  • 21. enum MaybeOwned<'a> { Slice(&'a str), Owned(String), } ! fn from_utf8_lossy<'a>(v: &'a [u8]) -> MaybeOwned<'a> { … } MaybeOwned
  • 22. 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
  • 23. 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
  • 24. 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
  • 25. 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++
  • 27. fn foo(xs: &mut Vec<int>) { for p in xs.iter() { if *p == 0 { xs.push(1); } } } Mutability: Rust
  • 29. 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 time tmp2.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 ends tmp2.rs:3 let b = &mut a; ^ tmp2.rs:5:2: 5:2 note: previous borrow ends here Cannot borrow as mutable twice
  • 30. 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 borrowed mut.rs:5 a = 3; ^ mut.rs:4:13: 4:15 note: borrow of `a` occurs here mut.rs:4 let b = &a; ^~ Cannot assign, because borrowed
  • 31. Smart Pointers C++ Rust std::unique_ptr<T> Box<T> std::shared_ptr<T> Rc<T> or Arc<T>
  • 32. struct Foo { v: int, } ! let ptr = Rc::new(Foo { … }); println!("{}", ptr.v); User-defined pointers: Rc<T>
  • 33. 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
  • 34. Threads • tasks • channels • Arc • AtomicInt • Mutex
  • 35. 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
  • 36. 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
  • 37. // similar to Rc<T> except // Arc uses atomic counter, not plain // data is immutable inside Arc // so Arc can be safely shared between threads let 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>
  • 38. Mutex<T> • Mutex<T> = T + mutex • Safely share data between threads
  • 39. 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>
  • 40. 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
  • 42. Program safety 0 25 50 75 100 Performance of compiled code C++ Rust Java
  • 43. Development speed 0 25 50 75 100 Speed of development C++ Rust Java
  • 44. Problems • compile-time metaprogramming • IDE • incremental compilation
  • 45. 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)
  • 46. 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
  • 47. 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
  • 48. class FileInputStream: public InputStream { int fd; } ! // is actually ! struct FileInputStream { void* vtablePtr; int fd; } C++/Java vtable
  • 49. 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