August 10, 2015
Kevin Yeh && Sam Rossi
"Rust is a systems programming language that runs blazingly fast, prevents nearly all segfaults, and guarantees thread safety."
Zero-cost abstractions between Rust and other languages.
Communicates with C, Python, Ruby, Javascript, and many other languages without overhead.
Provides C-level performance improvements while leveraging Rust-specific safety guarantees.
How would you query all the players on a certain team?
{
"_id" : ObjectId("55a02f52648dca06dce7e5d0"),
"first_name" : "Jose",
"last_name" : "Alvarez",
"bats" : "L",
"throws" : "L",
"team" : "LAA",
"position" : "P",
"avg" : null,
"tags" : [ ]
}
Sample
Document
let db = client.db("mlb");
let coll = db.collection("players");
let filter = Some(doc! { "team" => team });
let mut options = FindOptions::new();
options.projection = Some(doc! {
"_id" => 0,
"first_name" => 1,
"last_name" => 1,
"position" => 1
});
match coll.find(filter, Some(options)) {
Ok(cursor) => Ok(cursor),
Err(e) => err_as_string!(e),
}
Note: The code segment:
produces the BSON equivalent to the JSON object:
(more on that later)
doc! {
"team" => "BOS"
}
Step 1
{ "team": "BOS" }
let db = client.db("mlb");
let coll = db.collection("players");
let filter = Some(doc! { "team" => team });
let mut options = FindOptions::new();
options.projection = Some(doc! {
"_id" => 0,
"first_name" => 1,
"last_name" => 1,
"position" => 1
});
match coll.find(filter, Some(options)) {
Ok(cursor) => Ok(cursor),
Err(e) => err_as_string!(e),
}
Step 1
Step 2.2
Step 2.1
let db = client.db("mlb");
let coll = db.collection("players");
let filter = Some(doc! { "team" => team });
let mut options = FindOptions::new();
options.projection = Some(doc! {
"_id" => 0,
"first_name" => 1,
"last_name" => 1,
"position" => 1
});
match coll.find(filter, Some(options)) {
Ok(cursor) => Ok(cursor),
Err(e) => err_as_string!(e),
}
Step 1
Step 2.2
Step 2.1
Step 3
let mut string = "{\"result\":[".to_owned();
for (i, doc_result) in cursor.enumerate() {
match json_string_from_doc_result(doc_result) {
Ok(json_string) => {
let new_string = if i == 0 {
json_string
} else {
format!(",{}"), json_string)
};
string.push_str(&new_string);
},
Err(e) => return e,
}
}
string.push_str("]}");
`Cursor`
implements
`Iterator`
For Instance:
By default, all variables in Rust are immutable.
However, mutability is a bit different in Rust...
By default, all structs and variables in Rust have exterior immutability.
Immutable structures can still hold mutable components, as long as they follow the ownership system of Rust:
You may have one or the other of these two kinds of borrows, but not both at the same time:
one or more references (&T) to a resource.
exactly one mutable reference (&mut T).
One approach to guaranteeing these rules at compile time is to use RAII locks.
Acquire socket lock, Use socket, Release socket lock.
Lock the pool
Pop a stream (S)
Return the stream
with a pool reference
Not very readable!
let mut update = bson::Document::new();
let mut set = bson::Document::new();
set.insert("director".to_owned(), Bson::String("Robert Zemeckis".to_owned()));
update.insert("$set".to_owned(), Bson::Document(set));
Do we really need a second macro?
let update = doc! {
"$set" => nested_doc! {
"director" => Bson::String("Robert Zemeckis".to_owned())
}
};
#[macro_export]
macro_rules! doc {
( $( $k:expr => $v: expr),* ) => {
{
let mut doc = Document::new();
$(
doc.insert($k.to_owned(), $v);
)*
doc
}
};
}
#[macro_export]
macro_rules! nested_doc {
( $( $k:expr => $v: expr),* ) => {
Bson::Document(doc!(
$( $k => $v),*
))
}
}
Explicit types still needed
let update = doc! {
"$set" => {
"director" => Bson::String("Robert Zemeckis".to_owned())
}
};
#[macro_export]
macro_rules! add_to_doc {
($doc:expr, $key:expr => ($val:expr)) => {{
$doc.insert($key.to_owned(), $val);
}};
($doc:expr, $key:expr => [$($val:expr),*]) => {{
let vec = vec![$($val),*];
$doc.insert($key.to_owned(), Bson::Array(vec));
}};
($doc:expr, $key:expr => { $($k:expr => $v:tt),* }) => {{
$doc.insert($key.to_owned(), Bson::Document(doc! {
$(
$k => $v
),*
}));
}};
}
#[macro_export]
macro_rules! doc {
( $($key:expr => $val:tt),* ) => {{
let mut document = Document::new();
$(
add_to_doc!(document, $key => $val);
)*
document
}};
}
let doc1 = doc! { "tags" => ["a", "b", "c"] };
let doc2 = doc! { "tags" => ["a", "b", "d"] };
let doc3 = doc! { "tags" => ["d", "e", "f"] };
coll.insert_many(vec![doc1.clone(), doc2.clone(), doc3.clone()], false, None)
.ok().expect("Failed to execute insert_many command.");
// Build aggregation pipeline to unwind tag arrays and group distinct tags
let project = doc! { "$project" => { "tags" => 1 } };
let unwind = doc! { "$unwind" => ("$tags") };
let group = doc! { "$group" => { "_id" => "$tags" } };
#[macro_export]
macro_rules! bson {
([$($val:tt),*]) => {{
let mut array = Vec::new();
$(
array.push(bson!($val));
)*
$crate::Bson::Array(array)
}};
([$val:expr]) => {{
$crate::Bson::Array(vec!(::std::convert::From::from($val)))
}};
({ $($k:expr => $v:tt),* }) => {{
$crate::Bson::Document(doc! {
$(
$k => $v
),*
})
}};
($val:expr) => {{
::std::convert::From::from($val)
}};
}
#[macro_export]
macro_rules! doc {
() => {{ $crate::Document::new() }};
( $($key:expr => $val:tt),* ) => {{
let mut document = $crate::Document::new();
$(
document.insert($key.to_owned(), bson!($val));
)*
document
}};
}
Auto-converted types!
Conversion Traits: Auto-convert types.
Encoder Traits: Auto-encode structs.
Error Traits: Handle errors unobtrusively.
Dereference Traits: Utilize the auto-dereferencing system.
Iterator Traits: Iterate naturally over custom structs.
I/O Traits: Perform I/O ops over custom structs.
...and speed!
Suppose you had the following Python function...
def sum_of_first_three_elements(num_list):
if len(num_list) < 3:
return None
return sum(num_list[:3])
How would you implement that in Rust?
# Python
sum(num_list[:3])
// Rust
num_list[0] + num_list[1] + num_list[2]
There's no "null" in Rust!
pub enum Option<T> {
None,
Some(T),
}
fn sum_of_first_three_elements(num_list: &[i32]) -> Option<i32> {
if num_list.len() < 3 {
None
} else {
Some(num_list[0] + num_list[1] + num_list[2])
}
}
Example function implemented in Rust
It might return an int...but it might not
"variants": almost like constructors
"enumerated type"
note: "T" is the generic type of the parameter
match <expression> {
<first pattern> => <resulting expression>,
<second pattern> => <resulting expression>,
...
// Optional catch-all
_ => <resulting expression>
}
match maybe_an_int {
Some(i) => println!("The integer is: {}", i),
None => println!("There is no integer :(")
}
fn say_hey(name: Option<&str>) {
let person = match name {
Some(string) => string,
None => "you"
};
println!("Hey, {}!", person);
}
say_hey(Some("Joe"); // prints "Hey, Joe!"
say_hey(None); // prints "Hey, you!"
pub enum Bson {
FloatingPoint(f64),
I32(i32),
I64(i64),
Binary(BinarySubtype, Vec<u8>),
// other types omitted
}
match bson_document.get("some_key") {
// Explicit fallthrough, and ignored capture of single variable
Some(&Bson::I32(_)) |
Some(&Bson::I64(_)) => println("I got an int!"),
// Multi-line results
Some(&Bson::FloatingPoint(f)) => {
let i = f as i32;
println!("I didn't get an int...but if I did, it would look like this: {}", f);
}
// Ignored capture of multiple variables
Some(&Bson::Binary { .. }) => println!("I got some bits"),
// "wildcard" matches everything else
_ => println!("I got a rock...")
}
impl Bson {
fn print_value(&self) {
match self {
Bson::I32(i) => println!("{}", i),
Bson::I64(i) => println!("{}", i),
Bson::FloatingPoint(f) => println!("{}", f),
...
}
}
}
let bson_int = Bson::I32(17);
bson_int.print_value(); // prints "17"
bson library
OID generation
stable macro
wire protocol
CRUD, commands, and cursors
bulk writes
connection pooling
GridFS
server discovery and monitoring (SDAM)
command monitoring (APM)
SCRAM-SHA-1 auth
server selection & failover
shard tagging and other server commands
SSL Support
Other auth mechanisms
__