From 6f94774e1c23be2a29a5149efb0dff817ed55cdf Mon Sep 17 00:00:00 2001 From: Emerentius Date: Fri, 31 Aug 2018 05:43:09 +0200 Subject: [PATCH] accumulate: expand tests to require general solutions The previous tests let students pass with hard-coded input and output types and needlessly specific trait bounds. --- config.json | 4 +- exercises/accumulate/.meta/hints.md | 9 ++- exercises/accumulate/README.md | 9 ++- exercises/accumulate/example.rs | 11 ++-- exercises/accumulate/tests/accumulate.rs | 74 +++++++++++------------- 5 files changed, 58 insertions(+), 49 deletions(-) diff --git a/config.json b/config.json index 625573105..9d1d36db6 100644 --- a/config.json +++ b/config.json @@ -481,7 +481,9 @@ "unlocked_by": "luhn", "difficulty": 4, "topics": [ - "function_pointer" + "generics", + "higher_order_functions", + "move_semantics" ] }, { diff --git a/exercises/accumulate/.meta/hints.md b/exercises/accumulate/.meta/hints.md index 5cbc3dc72..c7a19628e 100644 --- a/exercises/accumulate/.meta/hints.md +++ b/exercises/accumulate/.meta/hints.md @@ -1,7 +1,14 @@ ## Hints -It may help to look at the [Fn trait](https://doc.rust-lang.org/std/ops/trait.Fn.html). +It may help to look at the Fn\* traits: +[Fn](https://doc.rust-lang.org/std/ops/trait.Fn.html), +[FnMut](https://doc.rust-lang.org/std/ops/trait.Fn.html) and +[FnOnce](https://doc.rust-lang.org/std/ops/trait.Fn.html). Help with passing a closure into a function may be found in the ["closures as input parameters" section](https://doc.rust-lang.org/stable/rust-by-example/fn/closures/input_parameters.html) of [Rust by Example](https://doc.rust-lang.org/stable/rust-by-example/). + +The tests for this exercise will cause compile time errors, +if your function signature does not fit them, even when they're not run. +You may want to comment some tests out and generalize your solution piece by piece. diff --git a/exercises/accumulate/README.md b/exercises/accumulate/README.md index 445b0292c..735d0a628 100644 --- a/exercises/accumulate/README.md +++ b/exercises/accumulate/README.md @@ -27,12 +27,19 @@ Solve this one yourself using other basic tools instead. ## Hints -It may help to look at the [Fn trait](https://doc.rust-lang.org/std/ops/trait.Fn.html). +It may help to look at the Fn\* traits: +[Fn](https://doc.rust-lang.org/std/ops/trait.Fn.html), +[FnMut](https://doc.rust-lang.org/std/ops/trait.Fn.html) and +[FnOnce](https://doc.rust-lang.org/std/ops/trait.Fn.html). Help with passing a closure into a function may be found in the ["closures as input parameters" section](https://doc.rust-lang.org/stable/rust-by-example/fn/closures/input_parameters.html) of [Rust by Example](https://doc.rust-lang.org/stable/rust-by-example/). +The tests for this exercise will cause compile time errors, +if your function signature does not fit them, even when they're not run. +You may want to comment some tests out and generalize your solution piece by piece. + ## Rust Installation diff --git a/exercises/accumulate/example.rs b/exercises/accumulate/example.rs index 12fc94c82..c7b0c00a9 100644 --- a/exercises/accumulate/example.rs +++ b/exercises/accumulate/example.rs @@ -1,9 +1,10 @@ -pub fn map(mut values: Vec, f: F) -> Vec +pub fn map(values: Vec, mut f: F) -> Vec where - F: Fn(i32) -> i32, + F: FnMut(T) -> U, { - for val in &mut values { - *val = f(*val); + let mut v = Vec::with_capacity(values.len()); + for val in values { + v.push(f(val)); } - values + v } diff --git a/exercises/accumulate/tests/accumulate.rs b/exercises/accumulate/tests/accumulate.rs index f95e03c2e..a5350a5fb 100644 --- a/exercises/accumulate/tests/accumulate.rs +++ b/exercises/accumulate/tests/accumulate.rs @@ -7,7 +7,7 @@ fn square(x: i32) -> i32 { } #[test] -fn test_func_square_single() { +fn func_single() { let input = vec![2]; let expected = vec![4]; assert_eq!(map(input, square), expected); @@ -15,7 +15,7 @@ fn test_func_square_single() { #[test] #[ignore] -fn test_func_square_short() { +fn func_multi() { let input = vec![2, 3, 4, 5]; let expected = vec![4, 9, 16, 25]; assert_eq!(map(input, square), expected); @@ -23,64 +23,56 @@ fn test_func_square_short() { #[test] #[ignore] -fn test_func_square_long_with_neg() { - let input = vec![2, -3, -2, 3, 4, 3, 4, 5, 100, 8, 16, 34]; - let expected = vec![4, 9, 4, 9, 16, 9, 16, 25, 10000, 64, 256, 1156]; - assert_eq!(map(input, square), expected); -} - -#[test] -#[ignore] -fn test_func_abs_value_with_neg() { - let input = vec![-3]; - let expected = vec![3]; - assert_eq!(map(input, i32::abs), expected); -} - -#[test] -#[ignore] -fn test_func_abs_value_long() { - let input = vec![-3, 5, -10, 4, 100, -1234, 55443]; - let expected = vec![3, 5, 10, 4, 100, 1234, 55443]; - assert_eq!(map(input, i32::abs), expected); +fn closure() { + let input = vec![2, 3, 4, 5]; + let expected = vec![4, 9, 16, 25]; + assert_eq!(map(input, |x| x * x), expected); } #[test] #[ignore] -fn test_closure_square_single() { - let input = vec![2]; - let expected = vec![4]; +fn closure_floats() { + let input = vec![2.0, 3.0, 4.0, 5.0]; + let expected = vec![4.0, 9.0, 16.0, 25.0]; assert_eq!(map(input, |x| x * x), expected); } #[test] #[ignore] -fn test_closure_square_short() { - let input = vec![2, 3, 4, 5]; - let expected = vec![4, 9, 16, 25]; - assert_eq!(map(input, |x| x * x), expected); +fn strings() { + let input = vec!["1".to_string(), "2".into(), "3".into()]; + let expected = vec!["11".to_string(), "22".into(), "33".into()]; + assert_eq!(map(input, |s| s.repeat(2)), expected); } #[test] #[ignore] -fn test_closure_square_long_with_neg() { - let input = vec![2, -3, -2, 3, 4, 3, 4, 5, 100, 8, 16, 34]; - let expected = vec![4, 9, 4, 9, 16, 9, 16, 25, 10000, 64, 256, 1156]; - assert_eq!(map(input, |x| x * x), expected); +fn change_in_type() { + let input: Vec<&str> = vec!["1", "2", "3"]; + let expected: Vec = vec!["1".into(), "2".into(), "3".into()]; + assert_eq!(map(input, |s| s.to_string()), expected); } #[test] #[ignore] -fn test_closure_abs_value_with_neg() { - let input = vec![-3]; - let expected = vec![3]; - assert_eq!(map(input, |x| x.abs()), expected); +fn mutating_closure() { + let mut counter = 0; + let input = vec![-2, 3, 4, -5]; + let expected = vec![2, 3, 4, 5]; + let result = map(input, + |x: i64| { + counter += 1; + x.abs() + }); + assert_eq!(result, expected); + assert_eq!(counter, 4); } #[test] #[ignore] -fn test_closure_abs_value_long() { - let input = vec![-3, 5, -10, 4, 100, -1234, 55443]; - let expected = vec![3, 5, 10, 4, 100, 1234, 55443]; - assert_eq!(map(input, |x| x.abs()), expected); +fn minimal_bounds_on_input_and_output() { + // must be able to accept arbitrary input and output types + struct Foo; + struct Bar; + map(vec![Foo], |_| Bar); }