mirror of
https://github.com/google/comprehensive-rust.git
synced 2025-05-21 01:43:12 +02:00
Be more consistent about tests vs. main (#2644)
The content slides all use `fn main`, with the exception of the testing segment. But with this change, where it makes sense exercises use tests instead, and not both tests and `fn main`. A small change in `book.js` supports running tests when a code sample does not have `fn main` but does have `#[test]`, so these work naturally. Fixes #1581.
This commit is contained in:
parent
699c5137c7
commit
44a79741ff
@ -8,6 +8,27 @@ the [instructions in the README].
|
|||||||
|
|
||||||
[instructions in the README]: README.md#building
|
[instructions in the README]: README.md#building
|
||||||
|
|
||||||
|
## Writing Exercises
|
||||||
|
|
||||||
|
Each segment ends with an exercise. Exercises are typically structured as an
|
||||||
|
`exercise.rs` containing the problem and solution. This is referenced from
|
||||||
|
`exercise.md` and `solution.md`, using `{{#include exercise.rs:anchor_name}}` to
|
||||||
|
match ANCHOR comments in the `exercise.rs` file. Each segment also has a
|
||||||
|
`Cargo.toml` file containing a `[[bin]]` or `[lib]` section referring to
|
||||||
|
`exercise.rs`, and that Cargo package is referenced from the workspace the root
|
||||||
|
`Cargo.toml`. The result is that `exercise.rs` is built and tested by
|
||||||
|
`cargo test`.
|
||||||
|
|
||||||
|
For segments on day 1, exercises should use `fn main() { .. }` and `println!`,
|
||||||
|
with students visually verifying the correct output. On subsequent days, prefer
|
||||||
|
tests and omit `fn main() { .. }`. However, where tests would be difficult and
|
||||||
|
visual verification is more natural (such as in the Logger exercise), using
|
||||||
|
`fn main { .. }` is OK.
|
||||||
|
|
||||||
|
Especially for exercises without tests, consider including tests in
|
||||||
|
`exercise.rs` that do not appear in either `exercise.md` or `solution.md`, as
|
||||||
|
these can ensure the solution is correct.
|
||||||
|
|
||||||
## Testing
|
## Testing
|
||||||
|
|
||||||
We test the course material in several ways:
|
We test the course material in several ways:
|
||||||
|
@ -4,6 +4,6 @@ version = "0.1.0"
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
publish = false
|
publish = false
|
||||||
|
|
||||||
[[bin]]
|
[lib]
|
||||||
name = "borrowing"
|
name = "borrowing"
|
||||||
path = "../../third_party/rust-on-exercism/health-statistics.rs"
|
path = "../../third_party/rust-on-exercism/health-statistics.rs"
|
||||||
|
@ -9,10 +9,7 @@ minutes: 20
|
|||||||
Copy the code below to <https://play.rust-lang.org/> and fill in the missing
|
Copy the code below to <https://play.rust-lang.org/> and fill in the missing
|
||||||
method:
|
method:
|
||||||
|
|
||||||
```rust
|
```rust,editable
|
||||||
// TODO: remove this when you're done with your implementation.
|
|
||||||
#![allow(unused_variables, dead_code)]
|
|
||||||
|
|
||||||
{{#include ../../third_party/rust-on-exercism/health-statistics.rs:setup}}
|
{{#include ../../third_party/rust-on-exercism/health-statistics.rs:setup}}
|
||||||
|
|
||||||
{{#include ../../third_party/rust-on-exercism/health-statistics.rs:User_visit_doctor}}
|
{{#include ../../third_party/rust-on-exercism/health-statistics.rs:User_visit_doctor}}
|
||||||
@ -20,7 +17,5 @@ method:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
{{#include ../../third_party/rust-on-exercism/health-statistics.rs:main}}
|
|
||||||
|
|
||||||
{{#include ../../third_party/rust-on-exercism/health-statistics.rs:tests}}
|
{{#include ../../third_party/rust-on-exercism/health-statistics.rs:tests}}
|
||||||
```
|
```
|
||||||
|
@ -4,7 +4,7 @@ Building on the generic logger from this morning, implement a `Filter` which
|
|||||||
uses a closure to filter log messages, sending those which pass the filtering
|
uses a closure to filter log messages, sending those which pass the filtering
|
||||||
predicate to an inner logger.
|
predicate to an inner logger.
|
||||||
|
|
||||||
```rust,compile_fail
|
```rust,compile_fail,editable
|
||||||
{{#include exercise.rs:setup}}
|
{{#include exercise.rs:setup}}
|
||||||
|
|
||||||
// TODO: Define and implement `Filter`.
|
// TODO: Define and implement `Filter`.
|
||||||
|
@ -30,7 +30,5 @@ initial `n`.
|
|||||||
todo!("Implement this")
|
todo!("Implement this")
|
||||||
}
|
}
|
||||||
|
|
||||||
{{#include exercise.rs:tests}}
|
|
||||||
|
|
||||||
{{#include exercise.rs:main}}
|
{{#include exercise.rs:main}}
|
||||||
```
|
```
|
||||||
|
@ -25,15 +25,14 @@ fn collatz_length(mut n: i32) -> u32 {
|
|||||||
len
|
len
|
||||||
}
|
}
|
||||||
|
|
||||||
// ANCHOR: tests
|
// ANCHOR: main
|
||||||
|
fn main() {
|
||||||
|
println!("Length: {}", collatz_length(11)); // should be 15
|
||||||
|
}
|
||||||
|
// ANCHOR_END: main
|
||||||
|
// ANCHOR_END: solution
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_collatz_length() {
|
fn test_collatz_length() {
|
||||||
assert_eq!(collatz_length(11), 15);
|
assert_eq!(collatz_length(11), 15);
|
||||||
}
|
}
|
||||||
// ANCHOR_END: tests
|
|
||||||
|
|
||||||
// ANCHOR: main
|
|
||||||
fn main() {
|
|
||||||
println!("Length: {}", collatz_length(11));
|
|
||||||
}
|
|
||||||
// ANCHOR_END: main
|
|
||||||
|
@ -8,6 +8,6 @@ publish = false
|
|||||||
anyhow = "*"
|
anyhow = "*"
|
||||||
thiserror = "*"
|
thiserror = "*"
|
||||||
|
|
||||||
[[bin]]
|
[lib]
|
||||||
name = "parser"
|
name = "parser"
|
||||||
path = "exercise.rs"
|
path = "exercise.rs"
|
||||||
|
@ -88,25 +88,30 @@ fn eval(e: Expression) -> Result<i64, DivideByZeroError> {
|
|||||||
// ANCHOR_END: solution
|
// ANCHOR_END: solution
|
||||||
|
|
||||||
// ANCHOR: tests
|
// ANCHOR: tests
|
||||||
#[test]
|
#[cfg(test)]
|
||||||
fn test_error() {
|
mod test {
|
||||||
assert_eq!(
|
use super::*;
|
||||||
eval(Expression::Op {
|
|
||||||
op: Operation::Div,
|
|
||||||
left: Box::new(Expression::Value(99)),
|
|
||||||
right: Box::new(Expression::Value(0)),
|
|
||||||
}),
|
|
||||||
Err(DivideByZeroError)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
#[test]
|
||||||
let expr = Expression::Op {
|
fn test_error() {
|
||||||
op: Operation::Sub,
|
assert_eq!(
|
||||||
left: Box::new(Expression::Value(20)),
|
eval(Expression::Op {
|
||||||
right: Box::new(Expression::Value(10)),
|
op: Operation::Div,
|
||||||
};
|
left: Box::new(Expression::Value(99)),
|
||||||
println!("expr: {expr:?}");
|
right: Box::new(Expression::Value(0)),
|
||||||
println!("result: {:?}", eval(expr));
|
}),
|
||||||
|
Err(DivideByZeroError)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ok() {
|
||||||
|
let expr = Expression::Op {
|
||||||
|
op: Operation::Sub,
|
||||||
|
left: Box::new(Expression::Value(20)),
|
||||||
|
right: Box::new(Expression::Value(10)),
|
||||||
|
};
|
||||||
|
assert_eq!(eval(expr), Ok(10));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// ANCHOR_END: tests
|
// ANCHOR_END: tests
|
||||||
|
@ -4,6 +4,6 @@ version = "0.1.0"
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
publish = false
|
publish = false
|
||||||
|
|
||||||
[[bin]]
|
[lib]
|
||||||
name = "generics"
|
name = "generics"
|
||||||
path = "exercise.rs"
|
path = "exercise.rs"
|
||||||
|
@ -7,12 +7,12 @@ minutes: 10
|
|||||||
In this short exercise, you will implement a generic `min` function that
|
In this short exercise, you will implement a generic `min` function that
|
||||||
determines the minimum of two values, using the [`Ord`] trait.
|
determines the minimum of two values, using the [`Ord`] trait.
|
||||||
|
|
||||||
```rust,compile_fail
|
```rust,editable
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
|
|
||||||
// TODO: implement the `min` function used in `main`.
|
// TODO: implement the `min` function used in the tests.
|
||||||
|
|
||||||
{{#include exercise.rs:main}}
|
{{#include exercise.rs:tests}}
|
||||||
```
|
```
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
#![allow(dead_code)]
|
||||||
|
|
||||||
// ANCHOR: solution
|
// ANCHOR: solution
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
@ -22,15 +23,22 @@ fn min<T: Ord>(l: T, r: T) -> T {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ANCHOR: main
|
// ANCHOR: tests
|
||||||
fn main() {
|
#[test]
|
||||||
|
fn integers() {
|
||||||
assert_eq!(min(0, 10), 0);
|
assert_eq!(min(0, 10), 0);
|
||||||
assert_eq!(min(500, 123), 123);
|
assert_eq!(min(500, 123), 123);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn chars() {
|
||||||
assert_eq!(min('a', 'z'), 'a');
|
assert_eq!(min('a', 'z'), 'a');
|
||||||
assert_eq!(min('7', '1'), '1');
|
assert_eq!(min('7', '1'), '1');
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn strings() {
|
||||||
assert_eq!(min("hello", "goodbye"), "goodbye");
|
assert_eq!(min("hello", "goodbye"), "goodbye");
|
||||||
assert_eq!(min("bat", "armadillo"), "armadillo");
|
assert_eq!(min("bat", "armadillo"), "armadillo");
|
||||||
}
|
}
|
||||||
// ANCHOR_END: main
|
// ANCHOR_END: tests
|
||||||
|
@ -4,6 +4,6 @@ version = "0.1.0"
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
publish = false
|
publish = false
|
||||||
|
|
||||||
[[bin]]
|
[lib]
|
||||||
name = "offset-differences"
|
name = "offset_differences"
|
||||||
path = "exercise.rs"
|
path = "exercise.rs"
|
||||||
|
@ -11,7 +11,7 @@ Copy the following code to <https://play.rust-lang.org/> and make the tests
|
|||||||
pass. Use an iterator expression and `collect` the result to construct the
|
pass. Use an iterator expression and `collect` the result to construct the
|
||||||
return value.
|
return value.
|
||||||
|
|
||||||
```rust
|
```rust,editable
|
||||||
{{#include exercise.rs:offset_differences}}
|
{{#include exercise.rs:offset_differences}}
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
@ -51,5 +51,3 @@ fn test_degenerate_cases() {
|
|||||||
assert_eq!(offset_differences(1, empty), vec![]);
|
assert_eq!(offset_differences(1, empty), vec![]);
|
||||||
}
|
}
|
||||||
// ANCHOR_END: unit-tests
|
// ANCHOR_END: unit-tests
|
||||||
|
|
||||||
fn main() {}
|
|
||||||
|
@ -7,6 +7,6 @@ publish = false
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
thiserror = "*"
|
thiserror = "*"
|
||||||
|
|
||||||
[[bin]]
|
[lib]
|
||||||
name = "protobuf"
|
name = "protobuf"
|
||||||
path = "exercise.rs"
|
path = "exercise.rs"
|
||||||
|
@ -83,7 +83,7 @@ What remains for you is to implement the `parse_field` function and the
|
|||||||
|
|
||||||
// TODO: Implement ProtoMessage for Person and PhoneNumber.
|
// TODO: Implement ProtoMessage for Person and PhoneNumber.
|
||||||
|
|
||||||
{{#include exercise.rs:main }}
|
{{#include exercise.rs:tests }}
|
||||||
```
|
```
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
#![allow(dead_code)]
|
||||||
|
|
||||||
// ANCHOR: solution
|
// ANCHOR: solution
|
||||||
// ANCHOR: preliminaries
|
// ANCHOR: preliminaries
|
||||||
@ -193,21 +194,31 @@ impl<'a> ProtoMessage<'a> for PhoneNumber<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ANCHOR: main
|
// ANCHOR: tests
|
||||||
fn main() {
|
#[test]
|
||||||
|
fn test_id() {
|
||||||
let person_id: Person = parse_message(&[0x10, 0x2a]);
|
let person_id: Person = parse_message(&[0x10, 0x2a]);
|
||||||
assert_eq!(person_id, Person { name: "", id: 42, phone: vec![] });
|
assert_eq!(person_id, Person { name: "", id: 42, phone: vec![] });
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_name() {
|
||||||
let person_name: Person = parse_message(&[
|
let person_name: Person = parse_message(&[
|
||||||
0x0a, 0x0e, 0x62, 0x65, 0x61, 0x75, 0x74, 0x69, 0x66, 0x75, 0x6c, 0x20,
|
0x0a, 0x0e, 0x62, 0x65, 0x61, 0x75, 0x74, 0x69, 0x66, 0x75, 0x6c, 0x20,
|
||||||
0x6e, 0x61, 0x6d, 0x65,
|
0x6e, 0x61, 0x6d, 0x65,
|
||||||
]);
|
]);
|
||||||
assert_eq!(person_name, Person { name: "beautiful name", id: 0, phone: vec![] });
|
assert_eq!(person_name, Person { name: "beautiful name", id: 0, phone: vec![] });
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_just_person() {
|
||||||
let person_name_id: Person =
|
let person_name_id: Person =
|
||||||
parse_message(&[0x0a, 0x04, 0x45, 0x76, 0x61, 0x6e, 0x10, 0x16]);
|
parse_message(&[0x0a, 0x04, 0x45, 0x76, 0x61, 0x6e, 0x10, 0x16]);
|
||||||
assert_eq!(person_name_id, Person { name: "Evan", id: 22, phone: vec![] });
|
assert_eq!(person_name_id, Person { name: "Evan", id: 22, phone: vec![] });
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_phone() {
|
||||||
let phone: Person = parse_message(&[
|
let phone: Person = parse_message(&[
|
||||||
0x0a, 0x00, 0x10, 0x00, 0x1a, 0x16, 0x0a, 0x0e, 0x2b, 0x31, 0x32, 0x33,
|
0x0a, 0x00, 0x10, 0x00, 0x1a, 0x16, 0x0a, 0x0e, 0x2b, 0x31, 0x32, 0x33,
|
||||||
0x34, 0x2d, 0x37, 0x37, 0x37, 0x2d, 0x39, 0x30, 0x39, 0x30, 0x12, 0x04,
|
0x34, 0x2d, 0x37, 0x37, 0x37, 0x2d, 0x39, 0x30, 0x39, 0x30, 0x12, 0x04,
|
||||||
@ -221,8 +232,11 @@ fn main() {
|
|||||||
phone: vec![PhoneNumber { number: "+1234-777-9090", type_: "home" },],
|
phone: vec![PhoneNumber { number: "+1234-777-9090", type_: "home" },],
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Put that all together into a single parse.
|
// Put that all together into a single parse.
|
||||||
|
#[test]
|
||||||
|
fn test_full_person() {
|
||||||
let person: Person = parse_message(&[
|
let person: Person = parse_message(&[
|
||||||
0x0a, 0x07, 0x6d, 0x61, 0x78, 0x77, 0x65, 0x6c, 0x6c, 0x10, 0x2a, 0x1a,
|
0x0a, 0x07, 0x6d, 0x61, 0x78, 0x77, 0x65, 0x6c, 0x6c, 0x10, 0x2a, 0x1a,
|
||||||
0x16, 0x0a, 0x0e, 0x2b, 0x31, 0x32, 0x30, 0x32, 0x2d, 0x35, 0x35, 0x35,
|
0x16, 0x0a, 0x0e, 0x2b, 0x31, 0x32, 0x30, 0x32, 0x2d, 0x35, 0x35, 0x35,
|
||||||
@ -243,4 +257,4 @@ fn main() {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
// ANCHOR_END: main
|
// ANCHOR_END: tests
|
||||||
|
@ -18,7 +18,7 @@ implementing that same trait, adding behavior in the process. In the "Generics"
|
|||||||
segment this afternoon, we will see how to make the wrapper generic over the
|
segment this afternoon, we will see how to make the wrapper generic over the
|
||||||
wrapped type.
|
wrapped type.
|
||||||
|
|
||||||
```rust,compile_fail
|
```rust,compile_fail,editable
|
||||||
{{#include exercise.rs:setup}}
|
{{#include exercise.rs:setup}}
|
||||||
|
|
||||||
// TODO: Implement the `Logger` trait for `VerbosityFilter`.
|
// TODO: Implement the `Logger` trait for `VerbosityFilter`.
|
||||||
|
@ -29,7 +29,7 @@ files in the `src` directory.
|
|||||||
|
|
||||||
Here's the single-module implementation of the GUI library:
|
Here's the single-module implementation of the GUI library:
|
||||||
|
|
||||||
```rust
|
```rust,editable
|
||||||
{{#include exercise.rs:single-module}}
|
{{#include exercise.rs:single-module}}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -4,6 +4,6 @@ version = "0.1.0"
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
publish = false
|
publish = false
|
||||||
|
|
||||||
[[bin]]
|
[lib]
|
||||||
name = "eval"
|
name = "eval"
|
||||||
path = "exercise.rs"
|
path = "exercise.rs"
|
||||||
|
@ -46,7 +46,7 @@ evaluate to `85`. We represent this as a much bigger tree:
|
|||||||
|
|
||||||
In code, we will represent the tree with two types:
|
In code, we will represent the tree with two types:
|
||||||
|
|
||||||
```rust,editable
|
```rust
|
||||||
{{#include exercise.rs:Operation}}
|
{{#include exercise.rs:Operation}}
|
||||||
|
|
||||||
{{#include exercise.rs:Expression}}
|
{{#include exercise.rs:Expression}}
|
||||||
|
@ -127,13 +127,3 @@ fn test_zeros() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
// ANCHOR_END: tests
|
// ANCHOR_END: tests
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let expr = Expression::Op {
|
|
||||||
op: Operation::Div,
|
|
||||||
left: Box::new(Expression::Value(10)),
|
|
||||||
right: Box::new(Expression::Value(2)),
|
|
||||||
};
|
|
||||||
println!("expr: {expr:?}");
|
|
||||||
println!("result: {:?}", eval(expr));
|
|
||||||
}
|
|
||||||
|
@ -7,7 +7,7 @@ minutes: 20
|
|||||||
We will create a few utility functions for 3-dimensional geometry, representing
|
We will create a few utility functions for 3-dimensional geometry, representing
|
||||||
a point as `[f64;3]`. It is up to you to determine the function signatures.
|
a point as `[f64;3]`. It is up to you to determine the function signatures.
|
||||||
|
|
||||||
```rust,compile_fail
|
```rust,compile_fail,editable
|
||||||
// Calculate the magnitude of a vector by summing the squares of its coordinates
|
// Calculate the magnitude of a vector by summing the squares of its coordinates
|
||||||
// and taking the square root. Use the `sqrt()` method to calculate the square
|
// and taking the square root. Use the `sqrt()` method to calculate the square
|
||||||
// root, like `v.sqrt()`.
|
// root, like `v.sqrt()`.
|
||||||
|
@ -4,6 +4,6 @@ version = "0.1.0"
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
publish = false
|
publish = false
|
||||||
|
|
||||||
[[bin]]
|
[lib]
|
||||||
name = "binary-tree"
|
name = "binary_tree"
|
||||||
path = "exercise.rs"
|
path = "exercise.rs"
|
||||||
|
@ -14,7 +14,7 @@ Implement the following types, so that the given tests pass.
|
|||||||
Extra Credit: implement an iterator over a binary tree that returns the values
|
Extra Credit: implement an iterator over a binary tree that returns the values
|
||||||
in order.
|
in order.
|
||||||
|
|
||||||
```rust,editable,ignore
|
```rust,compile_fail,editable
|
||||||
{{#include exercise.rs:types}}
|
{{#include exercise.rs:types}}
|
||||||
|
|
||||||
// Implement `new`, `insert`, `len`, and `has` for `Subtree`.
|
// Implement `new`, `insert`, `len`, and `has` for `Subtree`.
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
#![allow(dead_code)]
|
||||||
|
|
||||||
// ANCHOR: solution
|
// ANCHOR: solution
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
@ -96,14 +97,6 @@ impl<T: Ord> Node<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let mut tree = BinaryTree::new();
|
|
||||||
tree.insert("foo");
|
|
||||||
assert_eq!(tree.len(), 1);
|
|
||||||
tree.insert("bar");
|
|
||||||
assert!(tree.has(&"foo"));
|
|
||||||
}
|
|
||||||
|
|
||||||
// ANCHOR: tests
|
// ANCHOR: tests
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
@ -4,6 +4,6 @@ version = "0.1.0"
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
publish = false
|
publish = false
|
||||||
|
|
||||||
[[bin]]
|
[lib]
|
||||||
name = "std-traits"
|
name = "std_traits"
|
||||||
path = "exercise.rs"
|
path = "exercise.rs"
|
||||||
|
@ -9,12 +9,12 @@ In this example, you will implement the classic
|
|||||||
playground, and implement the missing bits. Only rotate ASCII alphabetic
|
playground, and implement the missing bits. Only rotate ASCII alphabetic
|
||||||
characters, to ensure the result is still valid UTF-8.
|
characters, to ensure the result is still valid UTF-8.
|
||||||
|
|
||||||
```rust,compile_fail
|
```rust,editable
|
||||||
{{#include exercise.rs:head }}
|
{{#include exercise.rs:head }}
|
||||||
|
|
||||||
// Implement the `Read` trait for `RotDecoder`.
|
// Implement the `Read` trait for `RotDecoder`.
|
||||||
|
|
||||||
{{#include exercise.rs:main }}
|
{{#include exercise.rs:tests }}
|
||||||
```
|
```
|
||||||
|
|
||||||
What happens if you chain two `RotDecoder` instances together, each rotating by
|
What happens if you chain two `RotDecoder` instances together, each rotating by
|
||||||
|
@ -36,15 +36,7 @@ impl<R: Read> Read for RotDecoder<R> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ANCHOR: main
|
// ANCHOR: tests
|
||||||
fn main() {
|
|
||||||
let mut rot =
|
|
||||||
RotDecoder { input: "Gb trg gb gur bgure fvqr!".as_bytes(), rot: 13 };
|
|
||||||
let mut result = String::new();
|
|
||||||
rot.read_to_string(&mut result).unwrap();
|
|
||||||
println!("{}", result);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
@ -72,4 +64,4 @@ mod test {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// ANCHOR_END: main
|
// ANCHOR_END: tests
|
||||||
|
@ -7,6 +7,6 @@ publish = false
|
|||||||
[lints.rust]
|
[lints.rust]
|
||||||
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(never)'] }
|
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(never)'] }
|
||||||
|
|
||||||
[[bin]]
|
[lib]
|
||||||
name = "luhn"
|
name = "luhn"
|
||||||
path = "exercise.rs"
|
path = "exercise.rs"
|
||||||
|
@ -27,7 +27,7 @@ correctly.
|
|||||||
Copy the code below to <https://play.rust-lang.org/> and write additional tests
|
Copy the code below to <https://play.rust-lang.org/> and write additional tests
|
||||||
to uncover bugs in the provided implementation, fixing any bugs you find.
|
to uncover bugs in the provided implementation, fixing any bugs you find.
|
||||||
|
|
||||||
```rust
|
```rust,editable
|
||||||
{{#include exercise.rs:luhn}}
|
{{#include exercise.rs:luhn}}
|
||||||
|
|
||||||
{{#include exercise.rs:unit-tests}}
|
{{#include exercise.rs:unit-tests}}
|
||||||
|
@ -11,8 +11,8 @@
|
|||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
#![allow(dead_code)]
|
||||||
|
|
||||||
// ANCHOR: solution
|
|
||||||
// This is the buggy version that appears in the problem.
|
// This is the buggy version that appears in the problem.
|
||||||
#[cfg(never)]
|
#[cfg(never)]
|
||||||
// ANCHOR: luhn
|
// ANCHOR: luhn
|
||||||
@ -40,6 +40,7 @@ pub fn luhn(cc_number: &str) -> bool {
|
|||||||
// ANCHOR_END: luhn
|
// ANCHOR_END: luhn
|
||||||
|
|
||||||
// This is the solution and passes all of the tests below.
|
// This is the solution and passes all of the tests below.
|
||||||
|
// ANCHOR: solution
|
||||||
pub fn luhn(cc_number: &str) -> bool {
|
pub fn luhn(cc_number: &str) -> bool {
|
||||||
let mut sum = 0;
|
let mut sum = 0;
|
||||||
let mut double = false;
|
let mut double = false;
|
||||||
@ -69,14 +70,6 @@ pub fn luhn(cc_number: &str) -> bool {
|
|||||||
digits >= 2 && sum % 10 == 0
|
digits >= 2 && sum % 10 == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let cc_number = "1234 5678 1234 5670";
|
|
||||||
println!(
|
|
||||||
"Is {cc_number} a valid credit card number? {}",
|
|
||||||
if luhn(cc_number) { "yes" } else { "no" }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ANCHOR: unit-tests
|
// ANCHOR: unit-tests
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
|
@ -8,7 +8,7 @@ Rust and Cargo come with a simple unit test framework. Tests are marked with
|
|||||||
`#[test]`. Unit tests are often put in a nested `tests` module, using
|
`#[test]`. Unit tests are often put in a nested `tests` module, using
|
||||||
`#[cfg(test)]` to conditionally compile them only when building tests.
|
`#[cfg(test)]` to conditionally compile them only when building tests.
|
||||||
|
|
||||||
```rust,editable,ignore
|
```rust,editable
|
||||||
fn first_word(text: &str) -> &str {
|
fn first_word(text: &str) -> &str {
|
||||||
match text.find(' ') {
|
match text.find(' ') {
|
||||||
Some(idx) => &text[..idx],
|
Some(idx) => &text[..idx],
|
||||||
@ -39,9 +39,3 @@ mod tests {
|
|||||||
|
|
||||||
- This lets you unit test private helpers.
|
- This lets you unit test private helpers.
|
||||||
- The `#[cfg(test)]` attribute is only active when you run `cargo test`.
|
- The `#[cfg(test)]` attribute is only active when you run `cargo test`.
|
||||||
|
|
||||||
<details>
|
|
||||||
|
|
||||||
Run the tests in the playground in order to show their results.
|
|
||||||
|
|
||||||
</details>
|
|
||||||
|
@ -26,15 +26,10 @@ transpose a matrix (turn rows into columns):
|
|||||||
Copy the code below to <https://play.rust-lang.org/> and implement the function.
|
Copy the code below to <https://play.rust-lang.org/> and implement the function.
|
||||||
This function only operates on 3x3 matrices.
|
This function only operates on 3x3 matrices.
|
||||||
|
|
||||||
```rust,should_panic
|
```rust,should_panic,editable
|
||||||
// TODO: remove this when you're done with your implementation.
|
|
||||||
#![allow(unused_variables, dead_code)]
|
|
||||||
|
|
||||||
{{#include exercise.rs:transpose}}
|
{{#include exercise.rs:transpose}}
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
{{#include exercise.rs:tests}}
|
|
||||||
|
|
||||||
{{#include exercise.rs:main}}
|
{{#include exercise.rs:main}}
|
||||||
```
|
```
|
||||||
|
@ -25,7 +25,23 @@ fn transpose(matrix: [[i32; 3]; 3]) -> [[i32; 3]; 3] {
|
|||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
// ANCHOR: tests
|
// ANCHOR: main
|
||||||
|
fn main() {
|
||||||
|
let matrix = [
|
||||||
|
[101, 102, 103], // <-- the comment makes rustfmt add a newline
|
||||||
|
[201, 202, 203],
|
||||||
|
[301, 302, 303],
|
||||||
|
];
|
||||||
|
|
||||||
|
println!("matrix: {:#?}", matrix);
|
||||||
|
let transposed = transpose(matrix);
|
||||||
|
println!("transposed: {:#?}", transposed);
|
||||||
|
}
|
||||||
|
// ANCHOR_END: main
|
||||||
|
// ANCHOR_END: solution
|
||||||
|
|
||||||
|
// This test does not appear in the exercise, as this is very early in the course, but it verifies
|
||||||
|
// that the solution is correct.
|
||||||
#[test]
|
#[test]
|
||||||
fn test_transpose() {
|
fn test_transpose() {
|
||||||
let matrix = [
|
let matrix = [
|
||||||
@ -43,18 +59,3 @@ fn test_transpose() {
|
|||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
// ANCHOR_END: tests
|
|
||||||
|
|
||||||
// ANCHOR: main
|
|
||||||
fn main() {
|
|
||||||
let matrix = [
|
|
||||||
[101, 102, 103], // <-- the comment makes rustfmt add a newline
|
|
||||||
[201, 202, 203],
|
|
||||||
[301, 302, 303],
|
|
||||||
];
|
|
||||||
|
|
||||||
println!("matrix: {:#?}", matrix);
|
|
||||||
let transposed = transpose(matrix);
|
|
||||||
println!("transposed: {:#?}", transposed);
|
|
||||||
}
|
|
||||||
// ANCHOR_END: main
|
|
||||||
|
@ -51,7 +51,7 @@ The [Nomicon] also has a very useful chapter about FFI.
|
|||||||
Copy the code below to <https://play.rust-lang.org/> and fill in the missing
|
Copy the code below to <https://play.rust-lang.org/> and fill in the missing
|
||||||
functions and methods:
|
functions and methods:
|
||||||
|
|
||||||
```rust,should_panic
|
```rust,should_panic,editable
|
||||||
// TODO: remove this when you're done with your implementation.
|
// TODO: remove this when you're done with your implementation.
|
||||||
#![allow(unused_imports, unused_variables, dead_code)]
|
#![allow(unused_imports, unused_variables, dead_code)]
|
||||||
|
|
||||||
|
@ -148,6 +148,11 @@ function playground_text(playground, hidden = true) {
|
|||||||
crateType: "bin",
|
crateType: "bin",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// If the code block has no `main` but does have tests, run those.
|
||||||
|
if (text.indexOf("fn main") === -1 && text.indexOf("#[test]") !== -1) {
|
||||||
|
params.tests = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (text.indexOf("#![feature") !== -1) {
|
if (text.indexOf("#![feature") !== -1) {
|
||||||
params.version = "nightly";
|
params.version = "nightly";
|
||||||
}
|
}
|
||||||
|
@ -50,13 +50,6 @@ impl User {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ANCHOR: main
|
|
||||||
fn main() {
|
|
||||||
let bob = User::new(String::from("Bob"), 32, 155.2);
|
|
||||||
println!("I'm {} and my age is {}", bob.name, bob.age);
|
|
||||||
}
|
|
||||||
// ANCHOR_END: main
|
|
||||||
|
|
||||||
// ANCHOR: tests
|
// ANCHOR: tests
|
||||||
#[test]
|
#[test]
|
||||||
fn test_visit() {
|
fn test_visit() {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user