&str
and String
.&str
&str
is a string slice (like array slice)."string literals"
are of type &str
.¹&str
s are statically-allocated and fixed-size.some_str[i]
, as each character may be multiple bytes
due to Unicode.chars()
:for c in "1234".chars() { ... }
¹More specifically, they have the type &'static str
.
String
#[derive(PartialOrd, Eq, Ord)]#[stable(feature = "rust1", since = "1.0.0")]pub struct String { vec: Vec<u8>,}
String
s are heap-allocated, and are dynamically growable.s.nth(i)
.&str
by taking a reference to the String
.let s0: String = String::new();let s1: String = "foo".to_string();let s2: String = String::from("bar");let and_s: &str = &s0;
Heap-allocated and growable like Vecs, because they are Vecs
str
&str
is the second string type, what exactly is str
?Unsized
type, meaning the size is unknown at compile time.str
s directly, only references.String
and an &str
may be concatenated with +
:let course_code = "CIS".to_string();let course_name = course_code + " 198";
String
s requires coercing one to &str
:let course_code = String::from("CIS");let course_num = String::from(" 198");let course_name = course_code + &course_num;
&str
s.let course_name = "CIS " + "198"; // Err!
String
& &str
: Why?Vec
s, &str
s are useful for passing a view into a String
.String
around, and lending an entire String
out
may be overkill.&str
therefore allows you to pass portions of a String
around, saving
memory.String
& &str
: Why?String
unless you have string literals.&str
s easily.String
via concatenation or formatting, then pass it as a
function argument.String
into an &str
requires a
dereference:use std::net::TcpStream;TcpStream::connect("192.168.0.1:3000"); // &strlet addr = "192.168.0.1:3000".to_string();TcpStream::connect(&*addr);
TcpStream
doesn't take an argument
of type &str
, but a Trait bounded type:TcpStream::connect<A: ToSocketAddr>(addr: A);
Deref
Coercions&T
to T
.pub trait Deref { type Target: ?Sized; fn deref(&self) -> &Self::Target;}
String
implements Deref<Target=str>
, so values of &String
will
automatically be dereferenced to &str
when possible.!
-
*
&
&mut
as
casting*
/
%
multiplicative arithmetic+
-
additive arithmetic<<
>>
shift arithmetic&
bitwise and^
bitwise xor|
bitwise or==
!=
<
>
<=
>=
logical comparison&&
logical and||
logical or=
..
assignment and rangescall()
, index[]
std::ops
.Neg
, Not
, Deref
, DerefMut
Mul
, Div
, Mod
Add
, Sub
Shl
, Shr
BitAnd
BitXor
BitOr
Eq
, PartialEq
, Ord
, PartialOrd
And
Or
Fn
, FnMut
, FnOnce
, Index
, IndexMut
, Drop
Option<T>
enum Option<T> { None, Some(T),}
NaN
, -1
, null
, etc. from a function.T
may be.Option::unwrap()
// fn foo() -> Option<i32>match foo() { None => None, Some(value) => { bar(value) // ... },}
Option::unwrap()
fn unwrap<T>(&self) -> T { // 🎁! match *self { None => panic!("Called `Option::unwrap()` on a `None` value"), Some(value) => value, }}let x = foo().unwrap();let y = bar(x);// ...
panic!
ing on None
values makes this abstraction inflexible.expect(&self, msg: String) -> T
instead.panic!
s with a custom error message if a None
value is found.Option::map()
Option
, change the value if it exists, and return an Option
.None
, we'll keep it as None
.fn map<U, F>(self, f: F) -> Option<U> where F: FnOnce(T) -> U { match self { None => None, Some(x) => Some(f(x)) }}// fn foo() -> Option<i32>let x = foo().map(|x| bar(x));
Option::and_then()
and_then
:fn and_then<U, F>(self, f: F) -> Option<U> where F: FnOnce(T) -> Option<U> { match self { Some(x) => f(x), None => None, }}// fn foo() -> Option<i32>let x = foo().and_then(|x| Some(bar(x)));
f
changes from T -> U
to T -> Some(U)
.Option::unwrap_or()
Option
value, but it has a sensible
default value, there's unwrap_or
.impl<T> Option<T> { fn unwrap_or<T>(&self, default: T) -> T { match *self { None => default, Some(value) => value, } }}
Option::unwrap_or_else()
impl<T> Option<T> { fn unwrap_or_else<T>(&self, f: F) -> T where F: FnOnce() -> T { match *self { None => f(), Some(value) => value, } }}
fn is_some(&self) -> bool
fn is_none(&self) -> bool
fn map_or<U, F>(self, default: U, f: F) -> U
where F: FnOnce(T) -> U
: U
.fn map_or_else<U, D, F>(self, default: D, f: F) -> U
where D: FnOnce() -> U, F: FnOnce(T) -> U
: D
.fn ok_or(self, err: E) -> Result<T, E>
fn ok_or_else(self, default: F) -> Result<T, E>
where F: FnOnce() -> E
unwrap_or
but returns a Result
with a default Err
or closure.fn and<U>(self, optb: Option<U>) -> Option<U>
None
if self
is None
, else optb
fn or(self, optb: Option<T>) -> Option<T>
self
if self
is Some(_)
, else optb
enum Result<T, E> { Ok(T), Err(E)}
Result
is like Option
, but it also encodes an Err
type.unwrap()
and expect()
methods.Option
using ok()
or err()
.Ok
or Err
and discards the other as None
.Option
and
, or
, unwrap
, etc.Option
, a Result
should always be consumed.Result
, you should be sure to unwrap
/expect
it, or otherwise handle the Ok
/Err
in a meaningful way.use std::io::Error;type Result<T> = Result<T, Error>;
E = Error
, this is
identical to std::Result
.use std::io;fn foo() -> io::Result { // ...}
try!
try!
is a macro, which means it generates Rust's code at compile-time.try!
generates looks roughly like this:macro_rules! try { ($e:expr) => (match $e { Ok(val) => val, Err(err) => return Err(err), });}try!(Ok(42));match Ok(42) { Ok(val) => val, Err(err) => return Err(err);}
try!
try!
is a concise way to implement early returns when encountering errors.let socket1: TcpStream = try!(TcpStream::connect("127.0.0.1:8000"));// Is equivalent to...let maybe_socket: Result<TcpStream> = TcpStream::connect("127.0.0.1:8000");let socket2: TcpStream = match maybe_socket { Ok(val) => val, Err(err) => { return Err(err) } };
try!
has some associated trait try!
in a function that returns a Result
.Vec<T>
VecDeque<T>
Vec
.LinkedList<T>
HashMap<K,V>
/BTreeMap<K,V>
HashMap<K, V>
is useful when you want a basic map.K: Hash + Eq
.BTreeMap<K, V>
is a sorted map (with different performance benefits).K: Ord
.HashSet<T>
/BTreeSet<T>
HashSet<T>
and BTreeSet<T>
are literally struct wrappers for HashMap<T, ()>
and BTreeMap<T, ()>
.BinaryHeap<T>
fn next() -> Option<T>
.Some(val)
until the end of the iterator (if it exists), then None
.collect
collect()
rolls a (lazy) iterator back into an actual collection.FromIterator
trait for the Item
inside the Iterator
.collect()
sometimes needs a type hint to properly compile.fn collect<B>(self) -> B where B: FromIterator<Self::Item>let vs = vec![1,2,3,4];// What type is this?let set = vs.iter().collect();// Hint to `collect` that we want a HashSet back.// Note the lack of an explicit <i32>.let set: HashSet<_> = vs.iter().collect();// Alternate syntax! The "turbofish" ::<>let set = vs.iter().collect::<HashSet<_>>();
fold
fn fold<B, F>(self, init: B, f: F) -> B where F: FnMut(B, Self::Item) -> B;let vs = vec![1,2,3,4,5];let sum = vs.iter().fold(0, |acc, &x| acc + x);assert_eq!(sum, 15);
fold
"folds up" an iterator into a single value.reduce
or inject
in other languages.fold
takes two arguments:acc
above) of type B
.B
and the type inside the iterator (Item
) and
returns a B
.fold
is implemented iteratively.filter
fn filter<P>(self, predicate: P) -> Filter<Self, P> where P: FnMut(&Self::Item) -> bool;
filter
takes a predicate function P
and removes anything that doesn't pass
the predicate.filter
returns a Filter<Self, P>
, so you need to collect
it to get a new
collection.skip
fn skip(self, n: usize) -> Skip<Self>;
n
elements.zip
fn zip<U>(self, other: U) -> Zip<Self, U::IntoIter> where U: IntoIterator;
a.iter().zip(b.iter())
.(ai, bi)
.any
& all
fn any<F>(&mut self, f: F) -> bool where F: FnMut(Self::Item) -> bool;fn all<F>(&mut self, f: F) -> bool where F: FnMut(Self::Item) -> bool;
any
tests if any element in the iterator matches the input functionall
tests all elements in the iterator match the input functionenumerate
fn enumerate(self) -> Enumerate<Self>;
enumerate
!(index, value)
pairs.index
is the usize
index of value
in the collection.for
loop to cause it to evaluate.map
fn map<B, F>(self, f: F) -> Map<Self, F> where F: FnMut(Self::Item) -> B;let vs = vec![1,2,3,4,5];let twice_vs: Vec<_> = vs.iter().map(|x| x * 2).collect();
map
takes a function and creates an iterator that calls the function on each
elementCollection<A>
and a function of A -> B
and
returns a Collection<B>
Collection
is not a real type)take
& take_while
fn take(self, n: usize) -> Take<Self>;fn take_while<P>(self, predicate: P) -> TakeWhile<Self, P> where P: FnMut(&Self::Item) -> bool;
take
creates an iterator that yields its first n
elements.take_while
takes a closure as an argument, and iterates until the closure
returns false
.for i in (0..).take(5) { println!("{}", i); // Prints 0 1 2 3 4}
cloned
fn cloned<'a, T>(self) -> Cloned<Self> where T: 'a + Clone, Self: Iterator<Item=&'a T>;
clone
on all of its elements.vs.iter().map(|v| v.clone())
.&T
, but need one over T
.drain
Iterator
method, but is very similar.drain()
on a collection removes and returns some or all elements.Vec::drain(&mut self, range: R)
removes and returns a range out of a vector.Iterator
methods we didn't cover.&str
and String
.Keyboard shortcuts
↑, ←, Pg Up, k | Go to previous slide |
↓, →, Pg Dn, Space, j | Go to next slide |
Home | Go to first slide |
End | Go to last slide |
b / m / f | Toggle blackout / mirrored / fullscreen mode |
c | Clone slideshow |
p | Toggle presenter mode |
t | Restart the presentation timer |
?, h | Toggle this help |
Esc | Back to slideshow |