1
0
mirror of https://github.com/google/comprehensive-rust.git synced 2025-05-21 09:53: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:
Dustin J. Mitchell 2025-02-18 15:13:16 -05:00 committed by GitHub
parent 699c5137c7
commit 44a79741ff
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
38 changed files with 138 additions and 144 deletions

View File

@ -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:

View File

@ -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"

View File

@ -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}}
``` ```

View File

@ -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`.

View File

@ -30,7 +30,5 @@ initial `n`.
todo!("Implement this") todo!("Implement this")
} }
{{#include exercise.rs:tests}}
{{#include exercise.rs:main}} {{#include exercise.rs:main}}
``` ```

View File

@ -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

View File

@ -8,6 +8,6 @@ publish = false
anyhow = "*" anyhow = "*"
thiserror = "*" thiserror = "*"
[[bin]] [lib]
name = "parser" name = "parser"
path = "exercise.rs" path = "exercise.rs"

View File

@ -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

View File

@ -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"

View File

@ -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>

View File

@ -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

View File

@ -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"

View File

@ -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!()
} }

View File

@ -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() {}

View File

@ -7,6 +7,6 @@ publish = false
[dependencies] [dependencies]
thiserror = "*" thiserror = "*"
[[bin]] [lib]
name = "protobuf" name = "protobuf"
path = "exercise.rs" path = "exercise.rs"

View File

@ -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>

View File

@ -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

View File

@ -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`.

View File

@ -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}}
``` ```

View File

@ -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"

View File

@ -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}}

View File

@ -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));
}

View File

@ -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()`.

View File

@ -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"

View File

@ -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`.

View File

@ -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 {

View File

@ -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"

View File

@ -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

View File

@ -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

View File

@ -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"

View File

@ -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}}

View File

@ -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 {

View File

@ -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>

View File

@ -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}}
``` ```

View File

@ -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

View File

@ -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)]

View File

@ -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";
} }

View File

@ -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() {