Rust: код может быть одновременно безопасным и быстрым, Степан Кольцов
- 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
- 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
- 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)
- 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
- 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
- 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
- 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>
- 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
- 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
- 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