Rust - newgeekorder/TechWiki GitHub Wiki
Basic hello world cargo file
[package]
name = "hello_world"
version ="0.0.1"
author = ["richard"]
[lib]
name="hello"
test = true
plugin = false
[[bin]]
name = "hello"
path = "src/hello.rs"
Cargo dependencies file
[dependencies]
advapi32-sys = "0.1"
curl = "0.2"
docopt = "0.6"
env_logger = "0.3"
filetime = "0.1"
flate2 = "0.2"
git2 = "0.2"
git2-curl = "0.2"
glob = "0.2"
kernel32-sys = "0.1"
libc = "0.1"
libgit2-sys = "0.2"
log = "0.3"
num_cpus = "0.2"
regex = "0.1"
registry = { path = "src/registry" }
rustc-serialize = "0.3"
semver = "0.1"
tar = { version = "0.2", features = ["nightly"] }
term = "0.2"
threadpool = "0.1"
time = "0.1"
toml = "0.1"
url = "0.2"
winapi = "0.1"
[dev-dependencies]
tempdir = "0.3"
hamcrest = { git = "https://github.com/carllerche/hamcrest-rust.git" }
bufstream = "0.1"
filetime = "0.1"
Creating a new Cargo executable folder
cargo new hello_world --bin
- Rust local variables are, by default,
- Immutable - meaning that you cannot change their assigned value once give
- Mutability - must be specifically defined
Basic variable declarations:
let foo = 5
and for mutable variables
let mut foo = 5;
foo = 6;
- rust compiler infers the type of of the declaration however explicit typing can be also provided
let x: int = 5; // int
let y: bool = false; // boolean
let z: char = 'x'; // char
Other primitives include
- i8, i16, i32, i64 - integer sizes
- u8, u16, u32, u64 - unsigned integers
- isize, usize - size depends on the size of a pointer of the underlying machine, signed or not
- f32, f64 - floating point integers
let a = [1, 2, 3]; // a: [i32; 3]
let mut m = [1, 2, 3]; // m: [i32; 3]
Short hand to initialize an array size, create an array of size 20 will be initialized to 0:
let a = [0; 20]; // a: [i32; 20]
Slices A ‘slice’ is a reference to (or “view” into) another data structure. They are useful for allowing safe, efficient access to a portion of an array without copying.
let a = [0, 1, 2, 3, 4];
let middle = &a[1..4]; // A slice of a: just the elements 1, 2, and 3
let complete = &a[..]; // A slice containing all of the elements in a
the str is the most primitive string type.. technicall a 'slice'
Rust also has a string Object
let s = "Hello, world.";
A rust string buffer:
let mut s = String::new(); // string buffer
let mut s = String::with_capacity(10); // fixed size
let s = String::from_str("hello"); // create string object from string literal
- rust string object has a number of the usual expected methods including:
- eq
- push
- len
- contains
The blogged [best practice] (http://smallcultfollowing.com/rust-int-variations/imem-umem/guide-strings.html) In general, you should prefer String when you need ownership, and &str when you just need to borrow a string. This is very similar to using Vec vs. &[T], and T vs &T in general.
This means starting off with this:
fn foo(s: &str) {
and only moving to this:
fn foo(s: String) {
If you have good reason. It's not polite to hold on to ownership you don't need, and it can make your lifetimes more complex.
- for
- while Rust supports a more groovy-ish looping syntax
for x in 0..10 {
println!("{}", x); // x: i32
}
Also loop on a collection
for var in expression {
code
}
Is also pretty normal with with optional braces
let mut x = 1;
while ( x < 10 ) {
println! ("x is {}", x);
x += 1 ;
}
you have the option to break and continue a loop with the usual words of
- break
- continue
Conditions are pretty normal except braces are optional
let x = 5;
if x == 5 {
println!("x is five!");
} else if x == 6 {
println!("x is six!");
} else {
println!("x is not five or six :(");
}
or you can use the more traditional
if (x == 5 ) {
Rust functions are pretty standard compared with other languages. However complications come in regarding the ownership of passed data. There is a good dog example of functions and ownership
fn foo(v: T) {
The key aspect is around the Rust memory management where the parameters passed may be
- copied and owned by the function
fn foo(v: T) {
- shared by the calling code and thememory borrowed
fn foo(v: &T) {
- borrowed by the function and allowed to mutate it
fn foo(v: &mut T) {
Function return are odd for 2 reasons
- types are slightly oddly defined with
->
- you have a choice of a return keyword and line terminated without a colon
- the return line are not terminated with a semi colon
fn add_one(x: i32) -> i32 {
x + 1 // this returns the result
}
you could also:
return x + 1 // with no semi colon
return x + 1: /// WITH a semi colon
If you do provide a semi-colon the compiler will say ```consider removing this semicolon:`` but throws and error and does not compile.
I have seen functions listed where there return multiple items
fn method1(a: &str) -> (String, String) {
let res = method2(a);
(res.val0(), res.val1())
}
Structs are ways to logically group data
struct Point {
x: i32,
y: i32,
}
Structures can be initalized with :
let mut point = Point3d { x: 0, y: 0, z: 0 };
Structs (for what ever reason) can also have methods associated (implemented) on them
struct Circle {
x: f64,
y: f64,
radius: f64,
}
impl Circle {
fn area(&self) -> f64 {
std::f64::consts::PI * (self.radius * self.radius)
}
}
fn main() {
let c = Circle { x: 0.0, y: 0.0, radius: 2.0 };
println!("{}", c.area());
}
HashMaps are part of use std::collections::HashMap;
testing indicates:
- in a struct the are initalized with a key and value type
pub struct config {
transport : String,
hostname : String,
output : String,
os_config : HashMap< String, String>
}
- in a function we can be more generic
let mut contacts = HashMap::new();
contacts.insert("Daniel", "798-1364");
HasMap get returns a result object :
match myConfig.os_config.get( &"KAFKA_CLIENT_ID".to_string() ){
Some(config) => { println! (" got back value {} ", config); },
None => { println! ("unknown value ") }
}
or one can skip the result object and get the value direct
myMap.get(&"hello".to_string()).unwrap()
however this does run the risk of throwing an exception for unknown values
thread '<main>' panicked at 'called `Option::unwrap()` on a `None` value'
An unknown error occurred
Searching faster
Searching a HashMap<String, ...> with a String can be expensive: if you don't already have one, it requires allocating a new String. We have a work around in the form of find_equiv, which allows you to pass a string literal (and, more generally, any &str) without allocating a new String:
use std::collections::HashMap;
fn main() {
let mut mymap = HashMap::new();
mymap.insert("foo".to_string(), "bar".to_string());
println!("{}", mymap.find_equiv(&"foo"));
println!("{}", mymap.find_equiv(&"not there"));
}
let guess: u32 = match guess.trim().parse() {
Ok(num) => num,
Err(_) => continue,
};
There is also a try!
macro as detailed in the blog
Rust has a number of methods and syntax extensions to indicate what is going on in closures Rust closures work like anonymous functions ..
let plus_one = |x: i32| x + 1;
assert_eq!(2, plus_one(1));
personally a bit of a pointless use case
Because Rust only likes to have one code section to have ownership, there is a move
keyword to force the ownership in to the closure
thread::spawn(move || { p.eat();
})
A number of methods return some variation of a Result
object. The output of which can be matched:
The mod keyword is used to manage module scope:
mod a {
pub fn foo() {}
}
mod b {
pub fn foo() {
super::a::foo(); // call a's foo function
}
}
- and the keyword super is used to begin resolution relative to the parent module
Module references
x;
x::y::z;
Modules are included with the '''extern''' and '''use''' include keywords By default rust pretends the following modules are include .. as if everything starts with
extern crate std;
use std::prelude::*;
- assert! - Ensure that a boolean expression is true at runtime.
- assert_eq! Asserts that two expressions are equal to each other.
- cfg! Boolean evaluation of configuration flags.
- column! A macro which expands to the column number on which it was invoked.
- concat! Concatenates literals into a static string slice.
- concat_idents! Concatenate identifiers into one identifier.
- debug_assert! Ensure that a boolean expression is true at runtime.
- debug_assert_eq! Asserts that two expressions are equal to each other, testing equality in both directions.
- env! Inspect an environment variable at compile time.
- file! A macro which expands to the file name from which it was invoked.
- format! Use the syntax described in std::fmt to create a value of type String. See std::fmt for more information.
- format_args! The core macro for formatted string creation & output.
- include! Parse the current given file as an expression.
- include_bytes! Includes a file as a byte slice.
- include_str! Includes a utf8-encoded file as a string.
- line! A macro which expands to the line number on which it was invoked.
- module_path! Expands to a string that represents the current module path.
- option_env! Optionally inspect an environment variable at compile time.
- panic! The entry point for panic of Rust threads.
- print! Macro for printing to the standard output.
- println! Macro for printing to the standard output.
- scoped_thread_local! Declare a new scoped thread local storage key.
- select! A macro to select an event from a number of receivers.
- stringify! A macro which stringifies its argument.
- thread_local! Declare a new thread local storage key of type std::thread::LocalKey.
- try! Helper macro for unwrapping Result values while returning early with an error if the value of the expression is Err. Can only be used in functions that return Result because of the early return of Err that it provides.
- unimplemented! A standardised placeholder for marking unfinished code. It panics with the message "not yet implemented" when executed.
- unreachable! A utility macro for indicating unreachable code.
- vec! Creates a Vec containing the arguments.
- write! Use the format! syntax to write data into a buffer of type &mut Write. See std::fmt for more information.
- writeln! Equivalent to the write! macro, except that a newline is appended after the message is written.
- spawn The spawn function takes in an owned function (which cannot capture any mutable, shared objects) and runs that function in a new thread:
fn spawn(f: proc())
- channels provide a way for spawned tasked to communicate back to their parent:
let (port, chan) : (Port<int>, Chan<int>) = Chan::new();
let val = node.head;
do spawn {
chan.send(f(val));
}
let newval = port.recv();
- The [thread] module contains Rust's threading abstractions.
- [sync] contains further primitive shared memory types, including:
- Rust Primitives
- [Rust Std Library] package
- Rust Language Refernece
- Rust Playpen
- [Rust Tutorial with Java Versions]](https://aml3.github.io/RustTutorial/html/01.html)
- Rust for Rubyists
- Rust Stdx Experiemental the most promising std extensions
- Rust by Example