Created
November 8, 2018 10:22
-
-
Save jorendorff/c04b6a2b56db051e0493de13acb77df0 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
mod sorting { | |
#[test] | |
fn sorting_integers() { | |
// Sorting a vector of integers is easy. | |
let mut integers = vec![8, 6, 7, 5, 3, 0, 9]; | |
integers.sort(); | |
assert_eq!(integers, vec![0, 3, 5, 6, 7, 8, 9]); | |
} | |
#[allow(dead_code)] | |
struct City { | |
name: String, | |
population: i64, | |
country: String, | |
lat_long: (f64, f64), | |
is_fictional: bool, | |
has_hot_chicken: bool, | |
monster_attack_risk: f64 | |
} | |
impl City { | |
fn new(t: (&'static str, i64, &'static str, (f64, f64), bool, bool, f64)) -> City { | |
City { | |
name: t.0.to_string(), | |
population: t.1, | |
country: t.2.to_string(), | |
lat_long: t.3, | |
is_fictional: t.4, | |
has_hot_chicken: t.5, | |
monster_attack_risk: t.6 | |
} | |
} | |
} | |
fn sample_data() -> Vec<City> { | |
[ | |
("Gainesville", 277163, "USA", (29.6500583, -82.3487493), false, false, 0.0), | |
("Athens", 203189, "USA", (33.9497366, -83.3733819), false, false, 0.0), | |
("Lexington", 500535, "USA", (38.0220905, -84.5053408), false, false, 0.0), | |
("Columbia, MO", 174974, "USA", (38.9359174, -92.3334619), false, false, 0.0), | |
("Columbia, SC", 810068, "USA", (33.9730840, -81.0187890), false, false, 0.0), | |
("Knoxville", 861424, "USA", (35.9525345, -83.9246578), false, false, 0.0), | |
("Nashville", 1830345, "USA", (36.1430984, -86.8085619), false, true, 13.2), | |
("Tuscaloosa", 239908, "USA", (33.2079134, -87.5496290), true, false, 0.0), | |
("Fayetteville", 513559, "USA", (36.0677677, -94.1778107), false, false, 0.0), | |
("Auburn", 156993, "USA", (32.6028163, -85.4901143), false, false, 0.0), | |
("Baton Rouge", 830480, "USA", (30.4117210, -91.1842500), false, false, 0.0), | |
("Starkville", 49800, "USA", (33.4563214, -88.7938700), false, false, 117.0), | |
("Oxford", 53154, "USA", (34.3618972, -89.5336923), false, false, 0.0), | |
("College Station", 249156, "USA", (30.6098417, -96.3404366), false, false, 0.0) | |
].iter().map(|&t| City::new(t)).collect() | |
} | |
#[test] | |
fn sort_with_key_fn() { | |
/// Helper function for sorting cities by population. | |
fn city_population_descending(city: &City) -> i64 { | |
-city.population | |
} | |
fn sort_cities(cities: &mut Vec<City>) { | |
cities.sort_by_key(city_population_descending); // ok | |
} | |
let mut cities = sample_data(); | |
sort_cities(&mut cities); | |
assert_eq!(cities[0].name, "Nashville"); | |
} | |
#[test] | |
fn sort_with_key_closure() { | |
fn sort_cities(cities: &mut Vec<City>) { | |
cities.sort_by_key(|city| -city.population); | |
} | |
let mut cities = sample_data(); | |
sort_cities(&mut cities); | |
assert_eq!(cities[0].name, "Nashville"); | |
} | |
#[derive(Clone, Copy, PartialEq, Eq)] | |
#[allow(dead_code)] | |
enum Statistic { | |
Population, | |
MonsterAttackRisk | |
} | |
impl City { | |
fn get_statistic(&self, stat: Statistic) -> i64 { | |
match stat { | |
Statistic::Population => self.population, | |
Statistic::MonsterAttackRisk => self.monster_attack_risk as i64 | |
} | |
} | |
} | |
#[test] | |
fn sort_closure() { | |
/// Sort by any of several different statistics. | |
fn sort_by_statistic(cities: &mut Vec<City>, stat: Statistic) { | |
cities.sort_by_key(|city| -city.get_statistic(stat)); | |
} | |
let mut cities = sample_data(); | |
sort_by_statistic(&mut cities, Statistic::Population); | |
assert_eq!(cities[0].name, "Nashville"); | |
} | |
#[test] | |
fn sort_on_thread() { | |
use std::thread; | |
fn start_sorting_thread(mut cities: Vec<City>, stat: Statistic) | |
-> thread::JoinHandle<Vec<City>> | |
{ | |
let key_fn = move |city: &City| -> i64 { -city.get_statistic(stat) }; | |
thread::spawn(move || { | |
cities.sort_by_key(key_fn); | |
cities | |
}) | |
} | |
let s = start_sorting_thread(sample_data(), Statistic::Population); | |
let results = s.join().unwrap(); | |
assert_eq!(results[0].name, "Nashville"); | |
} | |
#[test] | |
fn test_function_values_in_expr() { | |
/// Helper function for sorting cities by population. | |
fn city_population_descending(city: &City) -> i64 { | |
-city.population | |
} | |
fn city_monster_attack_risk_descending(city: &City) -> i64 { | |
-city.monster_attack_risk as i64 | |
} | |
struct Prefs { by_population: bool } | |
struct User { prefs: Prefs } | |
let user = User { prefs: Prefs { by_population: false } }; | |
let mut cities = sample_data(); | |
let my_key_fn: fn(&City) -> i64 = | |
if user.prefs.by_population { | |
city_population_descending | |
} else { | |
city_monster_attack_risk_descending | |
}; | |
cities.sort_by_key(my_key_fn); | |
assert_eq!(cities[0].name, "Starkville"); | |
} | |
#[test] | |
fn count_cities_by_function() { | |
let my_cities = sample_data(); | |
/// Given a list of cities and a test function, | |
/// return how many cities pass the test. | |
fn count_selected_cities(cities: &Vec<City>, | |
test_fn: fn(&City) -> bool) -> usize | |
{ | |
let mut count = 0; | |
for city in cities { | |
if test_fn(city) { | |
count += 1; | |
} | |
} | |
count | |
} | |
/// An example of a test function. Note that the type of | |
/// this function is `fn(&City) -> bool`, the same as | |
/// the `test_fn` argument to `count_selected_cities`. | |
fn has_monster_attacks(city: &City) -> bool { | |
city.monster_attack_risk > 0.0 | |
} | |
// How many cities are at risk for monster attack? | |
let n = count_selected_cities(&my_cities, has_monster_attacks); | |
assert_eq!(n, 2); | |
} | |
#[test] | |
fn count_cities_by_closure() { | |
fn count_selected_cities<F>(cities: &Vec<City>, test_fn: F) -> usize | |
where F: Fn(&City) -> bool | |
{ | |
let mut count = 0; | |
for city in cities { | |
if test_fn(city) { | |
count += 1; | |
} | |
} | |
count | |
} | |
let my_cities = sample_data(); | |
fn has_monster_attacks(city: &City) -> bool { | |
city.monster_attack_risk > 0.0 | |
} | |
let limit = 0.0000001; | |
count_selected_cities( | |
&my_cities, | |
has_monster_attacks); // ok | |
count_selected_cities( | |
&my_cities, | |
|city| city.monster_attack_risk > limit); // also ok | |
} | |
#[allow(dead_code)] | |
type CityFilterType = | |
fn(&City) -> bool // fn type (functions only) | |
; | |
#[allow(dead_code)] | |
type CityFilterTrait = | |
Fn(&City) -> bool // Fn trait (both functions and closures) | |
; | |
} | |
mod fnonce_closures { | |
#[test] | |
fn you_can_call_a_thing_twice() { | |
fn f() {} | |
f(); | |
f(); | |
} | |
fn call_twice<F>(closure: F) where F: Fn() { | |
closure(); | |
closure(); | |
} | |
#[test] | |
fn test_call_twice() { | |
fn do_nothing() {} | |
call_twice(do_nothing); | |
call_twice(|| {}); // do-nothing closure | |
} | |
} | |
mod fixing_a_fnonce_closure { | |
use std::collections::HashMap; | |
fn produce_glossary() -> HashMap<String, String> { | |
HashMap::new() | |
} | |
#[test] | |
fn test_dumping_dict_twice() { | |
let dict = produce_glossary(); | |
let debug_dump_dict = || { | |
for (key, value) in &dict { // does not use up dict | |
println!("{:?} - {:?}", key, value); | |
} | |
}; | |
debug_dump_dict(); | |
debug_dump_dict(); | |
} | |
} | |
mod fnmut_closures { | |
#[allow(dead_code)] | |
fn can_call_incr_twice_1() { | |
let mut i = 0; | |
let incr = || { | |
i += 1; // incr borrows a mut reference to i | |
println!("Ding! i is now: {}", i); | |
}; | |
call_twice(incr); | |
} | |
fn call_twice<F>(mut closure: F) where F: FnMut() { | |
closure(); | |
closure(); | |
} | |
#[test] | |
fn can_call_incr_twice_2() { | |
let mut i = 0; | |
call_twice(|| i += 1); // ok! | |
assert_eq!(i, 2); | |
} | |
} | |
#[test] | |
fn router_supports_closures() { | |
use router::Router; | |
use iron::prelude::{Iron, IronResult, Request, Response}; | |
use iron::status; | |
fn get_form_response() -> Response { | |
Response::with((status::Ok, "Hello World!")) | |
} | |
fn get_numbers(_request: &Request) -> IronResult<Vec<i64>> { | |
unimplemented!(); | |
} | |
fn get_gcd_response(_numbers: Vec<i64>) -> Response { | |
unimplemented!(); | |
} | |
let mut router = Router::new(); | |
router.get("/", |_: &mut Request| { | |
Ok(get_form_response()) | |
}, "root"); | |
router.post("/gcd", |request: &mut Request| { | |
let numbers = get_numbers(request)?; | |
Ok(get_gcd_response(numbers)) | |
}, "gcd"); | |
let _: Iron<Router> = Iron::new(router); | |
} | |
#[allow(dead_code)] | |
mod basic_router { | |
use std::collections::HashMap; | |
struct Request { | |
method: String, | |
url: String, | |
headers: HashMap<String, String>, | |
body: Vec<u8> | |
} | |
struct Response { | |
code: u32, | |
headers: HashMap<String, String>, | |
body: Vec<u8> | |
} | |
type BoxedCallback = Box<Fn(&Request) -> Response>; | |
struct BasicRouter { | |
routes: HashMap<String, BoxedCallback> | |
} | |
impl BasicRouter { | |
// Create an empty router. | |
fn new() -> BasicRouter { | |
BasicRouter { routes: HashMap::new() } | |
} | |
// Add a route to the router. | |
fn add_route<C>(&mut self, url: &str, callback: C) | |
where C: Fn(&Request) -> Response + 'static | |
{ | |
self.routes.insert(url.to_string(), Box::new(callback)); | |
} | |
} | |
impl BasicRouter { | |
fn handle_request(&self, request: &Request) -> Response { | |
match self.routes.get(&request.url) { | |
None => not_found_response(), | |
Some(callback) => callback(request) | |
} | |
} | |
} | |
fn not_found_response() -> Response { | |
Response { | |
code: 404, | |
headers: HashMap::new(), | |
body: b"<h1>Page not found</h1>".to_vec() | |
} | |
} | |
fn get_form_response() -> Response { | |
Response { | |
code: 200, | |
headers: HashMap::new(), | |
body: b"<form>".to_vec() | |
} | |
} | |
fn get_gcd_response(_req: &Request) -> Response { | |
Response { | |
code: 500, | |
headers: HashMap::new(), | |
body: b"<h1>Internal server error</h1>".to_vec() | |
} | |
} | |
fn req(url: &str) -> Request { | |
Request { | |
method: "GET".to_string(), | |
url: url.to_string(), | |
headers: HashMap::new(), | |
body: vec![] | |
} | |
} | |
#[test] | |
fn test_router() { | |
let mut router = BasicRouter::new(); | |
router.add_route("/", |_| get_form_response()); | |
router.add_route("/gcd", |req| get_gcd_response(req)); | |
assert_eq!(router.handle_request(&req("/piano")).code, 404); | |
assert_eq!(router.handle_request(&req("/")).code, 200); | |
assert_eq!(router.handle_request(&req("/gcd")).code, 500); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment