Compare commits

..

18 Commits

Author SHA1 Message Date
47ed183cfe cargo fmt 2026-05-26 14:42:05 +04:00
76cb232b1e Complete exercise: threads_channels 2026-05-26 14:38:45 +04:00
621fb6bb59 Complete exercise: logging 2026-05-26 00:08:56 +04:00
d745026c1a Complete exercise: testing 2026-05-25 16:27:58 +04:00
b25fe11bc7 Complete exercise: errors 2026-05-25 15:32:14 +04:00
89fd568b75 Complete exercise: traits 2026-05-25 15:07:20 +04:00
595ac0e75b Complete exercise: closures_iterators 2026-05-21 00:11:21 +04:00
c17cfe0169 Complete exercise: docs 2026-05-17 23:06:59 +04:00
7194a5d29e Complete exercise: idomatic 2026-05-17 22:45:35 +04:00
Nathan Stocks
5e852ca5d5 fix typo 2025-10-30 16:29:28 -06:00
Nathan Stocks
0b362a2a7b freshen up 2025-10-30 16:16:10 -06:00
Nathan Stocks
1b1ced37a4 just in case someone tries to use rusty_engine from within this repo 2023-02-10 20:24:11 -07:00
Magnus Markling
de71e01a95 even more 2022-09-19 14:11:25 -06:00
Magnus Markling
c100b83038 Remove more extra spaces 2022-09-19 14:11:25 -06:00
Magnus Markling
5c0dfcda33 Remove extra space from excercise 2022-09-19 14:11:25 -06:00
Jamil Lambert, PhD
ea93715738 Updated the comment to match the code
The code requires the student to create a Party not  a Dessert as originally stated in the comment
2022-09-14 10:31:47 -06:00
Korny666
036e7b7b50 // Silence some warnings so they don't distract from the exercise. clippy::vec_init_then_push is distracting here 2022-03-09 14:29:38 -07:00
Nathan Stocks
a47afbbb6e fix logic for logging step 5, output should now trigger two errors in a row 2022-02-20 14:20:49 -07:00
16 changed files with 285 additions and 130 deletions

View File

@@ -1,2 +1,3 @@
[workspace]
members = [ "example/*", "exercise/*" ]
resolver = "2"

View File

@@ -1,6 +1,6 @@
# Ultimate Rust 2: Intermediate Concepts
This is the companion repository for the `Ultimate Rust 2: Intermediate Concepts` (the followup to the popular [Ultimate Rust Crash Course]). _UR2IC_ will be published independently online in the second half of 2021 and is also presented live as part of some O'Reilly virtual events such as [Rust in 3 Weeks], or taught in-person for corporate training. You will get the most out of this training experience by doing the [exercises] in this repository and watching (or attending) the instructor-led training.
This is the companion repository for the `Ultimate Rust 2: Intermediate Concepts` (the followup to the popular [Ultimate Rust Crash Course]). You will get the most out of this training experience by doing the [exercises] in this repository and watching (or attending) the instructor-led training.
In other words, this repository is for you hands-on-learners!
@@ -22,7 +22,7 @@ The exercises are separate Rust projects inside the `exercises/` subdirectory.
- Open up the `src/main.rs` file.
- Follow the numbered exercise instructions in the code comments.
If you encounter any problems with the exercises, please feel free to use the online course communication tools to contact me, or [open an discussion]. Either way. 😄
If you encounter any problems with the exercises, please feel free to use the online course communication tools to contact me, or [open a discussion]. Either way. 😄
For your convenience, here is a list of all the exercises, with links to view the code on GitHub.
@@ -55,7 +55,7 @@ If you like the work I do, please consider sponsoring me [on GitHub] or [on Patr
[exercises]: https://github.com/CleanCut/ultimate_rust2#exercises
[`example/`]: https://github.com/CleanCut/ultimate_rust2/blob/main/example
[open an discussion]: https://github.com/CleanCut/ultimate_rust2/discussions/new
[open a discussion]: https://github.com/CleanCut/ultimate_rust2/discussions/new
[Ultimate Rust Crash Course]: https://agileperception.com/ultimate_rust_crash_course
[Rust in 3 Weeks]: https://agileperception.com
[Ultimate Rust 2: Intermediate Concepts]: https://github.com/CleanCut/ultimate_rust2

View File

@@ -6,30 +6,31 @@ fn main() {
// number multiplied by itself), and assign the closure to the "square" variable. Then run the
// code and make sure it works.
// let square = ...
// println!("5 squared is {}", square(5));
let square = |x| x * x;
println!("5 squared is {}", square(5));
// 2. Uncomment the code below. Finish the .map() iterator adaptor call by passing it a closure
// which takes a tuple of two integers as a parameter, and returns a tuple with the first
// integer incremented by 1, and the second integer left alone. For example, if given the input
// (0, 1), it should return (1, 1). Run the code and make sure it works.
// let pairs = vec![(0, 1), (2, 3), (4, 5)];
// pairs
// .into_iter()
// .map( ... )
// .for_each(|t| println!("{:?}", t));
let pairs = vec![(0, 1), (2, 3), (4, 5)];
pairs
.into_iter()
.map(|t| (t.0 + 1, t.1))
.for_each(|t| println!("{:?}", t));
// 3. Uncomment the code below. There is a mutable vector named `numbers`. Use an iterator over
// mutable references to multiply each of the values in `numbers` by 3.
// Hint 1: You'll need .iter_mut() -- bonus points if you use the shorter, syntactic sugar form!
// Hint 2: `x` will be a mutable reference, so remember to dereference it to use it
// let mut numbers = vec![1, 2, 3, 4];
// for x in ... {
// ... // multiply the value by 3 via the mutable reference x
// }
// println!("{:?}", numbers); // should print [3, 6, 9, 12]
let mut numbers = vec![1, 2, 3, 4];
// numbers = numbers.iter_mut().map(|x| *x * 3).collect();
for n in &mut numbers {
*n = *n * 3;
}
println!("{:?}", numbers); // should print [3, 6, 9, 12]
// 4. Uncomment the code below. Take the vector of words and
// - Convert the vector into an iterator with .into_iter()
@@ -39,9 +40,13 @@ fn main() {
//
// Hint: .to_uppercase() is a method on `str` which returns a String
// let words = vec!["autobot", "beach", "car", "decepticon", "energon", "frothy"];
// let transformed... // do the stuff here
// println!("Transformed: {:?}", transformed);
let words = vec!["autobot", "beach", "car", "decepticon", "energon", "frothy"];
let transformed = words
.into_iter()
.filter(|w| !w.contains("h"))
.map(|w| w.to_uppercase())
.collect::<Vec<String>>();
println!("Transformed: {:?}", transformed);
// Challenge:
//

View File

@@ -4,10 +4,17 @@
//
// Once you've got the documentation here, run `cargo doc --no-deps --open` and take a look!
//! A pumpkin is a cultivated winter squash in the genus Cucurbita.
//! The term is most commonly applied to round, orange-colored squash varieties,
//! but does not possess a scientific definition. It may be used in reference to
//! many different squashes of varied appearance and belonging to multiple species in the Cucurbita genus.
// 2. What about an image!? Add an image of a pumpkin to the end of the module-level documentation.
// The markdown format is ![some alt text](https://url-to-the-image.png)
// Here's the image to link to: https://upload.wikimedia.org/wikipedia/commons/thumb/5/5c/FrenchMarketPumpkinsB.jpg/700px-FrenchMarketPumpkinsB.jpg
//! ![Pumpkin Image](https://upload.wikimedia.org/wikipedia/commons/thumb/5/5c/FrenchMarketPumpkinsB.jpg/700px-FrenchMarketPumpkinsB.jpg)
// 3. Document the Pumpkin struct.
// - The description on the index page should be "Big orange thing"
// - Make a section header called "Recipes"
@@ -15,8 +22,16 @@
// - Document the "roundness" field, explaining that it is a percentage
// - Document the "orangeness" field, explaining that it is a number from 8 to 27
/// Big orange thing
///
/// # Recipes
///
/// Recipes will be coming soon.
pub struct Pumpkin {
/// `roundness` is a percentage that describes how round the pumpkin is.
/// 100% means perfectly round, while 0% means not round at all.
pub roundness: f32,
/// `orangeness` is a number from 8 to 27 that describes how orange the pumpkin is.
pub orangeness: i32,
}
@@ -24,12 +39,14 @@ pub struct Pumpkin {
// can't be used for pie. :'-(
impl Pumpkin {
/// Smash the pumpkin. Once smashed, it cannot be used for pie.
pub fn smash(self) {}
}
// 5. Document that BURNT_ORANGE is for the "orangeness" field in the Pumpkin struct.
// - Link to the Pumpkin struct in your description
/// The `BURNT_ORANGE` constant is a value of 13 that can be used for the `orangeness` field in the [Pumpkin] struct.
pub const BURNT_ORANGE: i32 = 13;
// Challenge: Find the option to pass to `cargo doc` so that documentation for this private item

View File

@@ -14,7 +14,18 @@
// Once you have completed defining the error type correctly, you should be able to run
// `cargo build --lib` without any build errors or warnings. Then go to main.rs and continue with #2
// pub enum DolphinError...
use thiserror::Error;
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum DolphinError {
#[error("The dolphin is hungry")]
Hungry,
#[error("The dolphin is too young")]
TooYoung,
#[error("The dolphin's name is too long and annoying to say")]
LongName,
}
pub struct Dolphin {
pub name: String,

View File

@@ -1,30 +1,34 @@
// START IN lib.rs!
use aquarium::Dolphin;
// Silence some warnings so they don't distract from the exercise.
#[allow(clippy::vec_init_then_push)]
// (You already did #1 in lib.rs, right?)
//
// 2a. Uncomment and finish the play_time function below
// - Bring anyhow::Result into scope with a `use` statement
// - Have the play_time function return a `Result<Vec<String>>`. The vector of Strings will
// represent successful outcomes of various dolphin tricks.
use anyhow::Result;
// fn play_time(dolphin: &Dolphin) -> ... {
// let mut responses = vec![];
// // 2b. Call the .say_your_name() method on `dolphin`, use `?` to unwrap the value, and push
// // the value onto the `responses` vector.
// //
// // let response = ... // this can be done with an intermediate variable...
// // responses.push( ... ) // ...or all on one line. Either way is fine!
// //
// // 2c. Do the same thing as #2b for the .flip() method
// //
// // 2d. Do the same thing as #2b for the .shake_hands() method
//
// Ok(responses)
// }
fn play_time(dolphin: &Dolphin) -> Result<Vec<String>> {
let mut responses = vec![];
// 2b. Call the .say_your_name() method on `dolphin`, use `?` to unwrap the value, and push
// the value onto the `responses` vector.
//
let response = dolphin.say_your_name()?; // this can be done with an intermediate variable...
responses.push(response); // ...or all on one line. Either way is fine!
//
// 2c. Do the same thing as #2b for the .flip() method
responses.push(dolphin.flip()?);
//
// 2d. Do the same thing as #2b for the .shake_hands() method
responses.push(dolphin.shake_hands()?);
fn main() {
Ok(responses)
}
fn main() -> Result<()> {
let dolphins = vec![
Dolphin {
name: "Augustinius".into(),
@@ -55,14 +59,23 @@ fn main() {
// returns an Err variant the first time it is called, the try operator will return it from
// main(), which will end the program at the first error. anyhow's Result will take care of
// formatting the error output for us.
match play_time(dolphin) {
Ok(responses) => {
println!("{} did a FABULOUS PERFORMANCE!", dolphin.name);
println!("\n{} did a FABULOUS PERFORMANCE!", dolphin.name);
for response in responses {
println!(" {}", response);
}
}
Err(e) => println!("{} can't perform today: {}", dolphin.name, e.to_string()),
}
// let responses = play_time(dolphin)?;
// println!("\n{} did a FABULOUS PERFORMANCE!", dolphin.name);
// for response in responses {
// println!(" {}", response);
// }
}
Ok(())
}

View File

@@ -8,16 +8,28 @@
// Challenge: Clippy doesn't find *everything*. What else would you change to make this code better?
const pi:f32=3.14159265358979323846;
fn count_to_5()->i32{let mut foo =0;loop{if foo>pi as i32{if foo > 5{break;}}foo=foo+1;}return 5;}
fn main() {
println!("I can count to {}", count_to_5());
use std::f32::consts::PI;
fn count_to_5() -> i32 {
let mut count = 0;
loop {
if count > PI as i32 && count >= 5 {
break;
}
count += 1;
}
count
}
fn main() {
println!("I can count to {}", count_to_5());
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_counting() {
assert_eq!(count_to_5() == 5, true);
}
use super::*;
#[test]
fn test_counting() {
assert_eq!(count_to_5() == 5, true);
}
}

View File

@@ -4,10 +4,5 @@ version = "0.1.0"
edition = "2021"
[dependencies]
# I'm glad you came to add the `log` dependency! I got it all ready for you, just uncomment:
#
# log = "0.4"
# And here's the env_logger dependency that you'll need in main.rs
#
# env_logger = "0.9"
log = "0.4" # I'm glad you came to add the `log` dependency! I got it all ready for you, just uncomment:
env_logger = "0.9" # And here's the env_logger dependency that you'll need in main.rs

View File

@@ -6,6 +6,8 @@
//
// Hint: You need to update Cargo.toml to add the `log` dependency, first.
use log::{debug, error, info, trace, warn};
#[derive(Debug)]
pub struct Frog {
energy: u8,
@@ -15,19 +17,24 @@ pub struct Frog {
impl Frog {
pub fn new() -> Self {
// 2. Use debug!() to log "A new Frog has been created"
debug!(target: "Frog::new", "A new Frog has been created");
Default::default()
}
pub fn hop(&mut self) {
self.energy -= 1;
// 3. Use info!() to log that a Frog hopped, and how much energy is left
info!(target: "Frog::hop", "A frog hopped! It has {} energy left", self.energy);
if self.energy == 0 {
// 4. Use warn!() to warn that the frog will go to sleep since he ran out of energy
warn!(target: "Frog::hop", "The frog will go to sleep since he ran out of energy");
self.sleep();
}
}
pub fn sleep(&mut self) {
if !self.sleeping {
if self.sleeping {
// 5. Use error!() to log a (non-fatal) error stating that the Frog is already asleep
error!(target: "Frog::sleep", "The frog is already asleep");
} else {
self.sleeping = true;
}
}
@@ -36,9 +43,12 @@ impl Frog {
impl Default for Frog {
fn default() -> Self {
// 6. Use trace!() to log that a default value was generated, with the debug representation
Frog {
let frog = Frog {
energy: 5,
sleeping: false,
}
};
trace!(target: "Frog::Default", "A default Frog value was generated: {:?}", frog);
frog
}
}

View File

@@ -8,6 +8,7 @@ use frogger::Frog;
fn main() {
// 8. Initialize env_logger using the init() function at the top level of the library
env_logger::init();
// 9. Run this program with `cargo run` and take a look at the default output.
// - Now run it again with an explicit log level, like `RUST_LOG=info cargo run`
@@ -19,6 +20,7 @@ fn main() {
skippy.hop();
skippy.hop();
skippy.sleep();
skippy.sleep();
// Challenge: Go back to lib.rs and set the `target: ` argument for each logging call to be the
// path to the function. For example, `Frog::new`

View File

@@ -9,15 +9,15 @@ edition = "2021"
# Challenge Help 1: If you choose to take on the challenge, you'll need to add `criterion` as a
# development dependency. Here is one way to do it:
# [dev-dependencies]
# criterion = { version = "0.3", features = ["html_reports"] }
[dev-dependencies]
criterion = { version = "0.3", features = ["html_reports"] }
# Challenge Help 2: Each benchmark needs a `[[bench]]` section with a name and disabling the harness.
# A name "somename" will correspond with a file "benches/somename.rs"
# [[bench]]
# name = "somename"
# harness = false
[[bench]]
name = "somename"
harness = false
# Challenge Help 3: The Criterion documentation has a great tutorial for how to actually write your
# benchmark. Don't skip the part about `black_box()`!

View File

@@ -0,0 +1,11 @@
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use testing::sploosh;
pub fn sploosh_benchmark(c: &mut Criterion) {
c.bench_function("sploosh", |b| {
b.iter(|| sploosh(black_box(8), black_box(9), black_box(10)))
});
}
criterion_group!(benches, sploosh_benchmark);
criterion_main!(benches);

View File

@@ -3,6 +3,7 @@ pub fn sploosh(x: i32, y: i32, z: i32) -> i32 {
(x, _, _) if x < 0 => 99,
(1, 2, 3) => 4,
(5, 6, 7) => 3,
(8, 9, 10) => 7,
(x, y, z) => x + y - z,
}
}
@@ -13,9 +14,11 @@ pub fn splish(a: i32, b: i32) -> i32 {
// 1. Use the `cfg` attribute to mark the `test` module below as a test module
#[cfg(test)]
mod test {
// 2. Bring all the library items into scope with a `use` statement
// Hint: It's okay to use `*` here.
use super::*;
// 3. Write a test function that verifies the following condition using the `assert_eq!` or
// `assert_ne!` macros
@@ -26,10 +29,24 @@ mod test {
// `cargo test` should run your tests and pass
// Hint: Don't forget the `#[test]` attribute for your test function!
#[test]
fn test_sploosh() {
assert_eq!(sploosh(1, 2, 3), 4);
assert_ne!(sploosh(5, 6, 7), 4);
assert_eq!(sploosh(-1, 2, 3), 99);
}
// 4. Write a test function that verifies the following conditions using the `assert!` macro
// - splish(100, 10) is negative
// - splish(40, 20) is positive
// - splish(9, 3) is 0
#[test]
fn test_splish() {
assert!(splish(100, 10) < 0);
assert!(splish(40, 20) > 0);
assert!(splish(9, 3) == 0);
}
}
// 5. Create a `tests/` directory and an integration test file `tests/more_tests.rs`

View File

@@ -0,0 +1,6 @@
use testing::{splish, sploosh};
#[test]
pub fn test_sploosh_splish() {
assert_eq!(sploosh(splish(-1, 0), splish(1, 1), splish(3, 2)), 4);
}

View File

@@ -22,8 +22,8 @@ fn main() {
// join handle in a variable called `handle`. Once you've done this you should be able to run
// the code and see the output from the child thread's expensive sum in the middle of the main
// thread's processing of letters.
//
//let handle = ...
let handle = thread::spawn(|| expensive_sum(my_vector));
// While the child thread is running, the main thread will also do some work
for letter in vec!["a", "b", "c", "d", "e", "f"] {
@@ -37,17 +37,16 @@ fn main() {
// to a variable named `result`
// - Get the i32 out of `result` and store it in a `sum` variable.
// let result =
// let sum =
// println!("The child thread's expensive sum is {}", sum);
let result = handle.join();
let sum = result.unwrap();
println!("The child thread's expensive sum is {}", sum);
// 3. Time for some fun with channels!
// - Uncomment the block comment below (Find and remove the `/*` and `*/`).
// - Create variables `tx` and `rx` and assign them to the sending and receiving ends of an
// unbounded channel. Hint: An unbounded channel can be created with `channel::unbounded()`
/*
// let ...
let (tx, rx) = channel::unbounded();
// Cloning a channel makes another variable connected to that end of the channel so that you can
// send it to another thread. We want another variable that can be used for sending...
@@ -62,7 +61,7 @@ fn main() {
// Thread A
let handle_a = thread::spawn(move || {
sleep_ms(0);
sleep_ms(500);
tx2.send("Thread A: 1").unwrap();
sleep_ms(200);
tx2.send("Thread A: 2").unwrap();
@@ -88,12 +87,40 @@ fn main() {
// 5. Oops, we forgot to join "Thread A" and "Thread B". That's bad hygiene!
// - Use the thread handles to join both threads without getting any compiler warnings.
*/
let _ = handle_a.join();
let _ = handle_b.join();
// Challenge: Make two child threads and give them each a receiving end to a channel. From the
// main thread loop through several values and print each out and then send it to the channel.
// On the child threads print out the values you receive. Close the sending side in the main
// thread by calling `drop(tx)` (assuming you named your sender channel variable `tx`). Join
// the child threads.
let (tx, rx) = channel::unbounded();
let rx2 = rx.clone();
let thread_a_handle = thread::spawn(move || {
for n in rx {
println!("Thread A received: {}", n);
sleep_ms(10);
}
});
let thread_b_handle = thread::spawn(move || {
for n in rx2 {
println!("Thread B received: {}", n);
sleep_ms(10);
}
});
for number in vec![1, 2, 3, 4, 5] {
println!("NUMBER: {}", number);
let _ = tx.send(number);
}
drop(tx);
let _ = thread_a_handle.join();
let _ = thread_b_handle.join();
println!("Main thread: Exiting.")
}

View File

@@ -1,15 +1,39 @@
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Cake {
Chocolate,
MapleBacon,
Spice,
}
#[derive(Debug)]
pub struct Party {
pub at_restaurant: bool,
pub num_people: u8,
pub cake: Cake,
}
impl Default for Party {
fn default() -> Self {
Self {
at_restaurant: true,
num_people: 8,
cake: Cake::Chocolate,
}
}
}
impl PartialEq for Party {
fn eq(&self, other: &Self) -> bool {
self.cake == other.cake
}
}
impl From<&Party> for Cake {
fn from(party: &Party) -> Self {
party.cake
}
}
fn main() {
// 1. The code below doesn't work because Cake doesn't implement Debug.
// - Derive the Debug trait for the Cake enum above so this code will work. Then, run the code.
@@ -23,11 +47,11 @@ fn main() {
// function instead of moved.
// - Hint: You may need to derive another trait in order to be able to derive the Copy trait
// match cake {
// Cake::Chocolate => println!("The name's Chocolate. Dark...Chocolate."),
// Cake::MapleBacon => println!("Dreams do come true!"),
// Cake::Spice => println!("Great, let's spice it up!"),
// }
match cake {
Cake::Chocolate => println!("The name's Chocolate. Dark...Chocolate."),
Cake::MapleBacon => println!("Dreams do come true!"),
Cake::Spice => println!("Great, let's spice it up!"),
}
// 3. Uncomment the println below. It doesn't work since the Party struct doesn't implement the
// Debug or Default traits.
@@ -44,18 +68,19 @@ fn main() {
// Hint: If you get stuck, there is an example at
// https://doc.rust-lang.org/std/default/trait.Default.html#how-can-i-implement-default
// println!("The default Party is\n{:#?}", Party::default());
println!("The default Party is\n{:#?}", Party::default());
// 4. You prefer Maple Bacon cake. Use "struct update syntax" to create a Dessert with `cake`
// 4. You prefer Maple Bacon cake. Use "struct update syntax" to create a Party with `cake`
// set to `Cake::MapleBacon`, but the rest of the values are default.
//
// Hint: The trick to struct update syntax is specifying the value(s) you want to customize
// first and then ending the struct with `..Default::default()` -- but no comma after that!
// let party = Party {
// ...
// };
// println!("Yes! My party has my favorite {:?} cake!", party.cake);
let party = Party {
cake: Cake::MapleBacon,
..Default::default()
};
println!("Yes! My party has my favorite {:?} cake!", party.cake);
// 5. Parties are "equal" if they have the same cake.
// - Derive the PartialEq trait for the Cake enum so Cakes can be compared.
@@ -63,14 +88,15 @@ fn main() {
// then they are equal, no matter the location or number of attendees at the party.
// - Uncomment and run the code below.
// let other_party = Party {
// at_restaurant: false,
// num_people: 235,
// cake: Cake::MapleBacon,
// };
// if party == other_party {
// println!("Your party is just like mine!");
// }
let other_party = Party {
at_restaurant: false,
num_people: 235,
cake: Cake::MapleBacon,
};
if party == other_party {
println!("Your party is just like mine!");
}
// Challenge: You would like to be able to pass a Party struct into the smell_cake() function
// which takes a type T which implements the Into<Cake> trait.
@@ -78,20 +104,22 @@ fn main() {
// - Implement `From<Party> for Cake` so that the function call below works.
//
// smell_cake(party);
smell_cake(&party);
// Challenge 2: Implement `From<&Party> for Cake` so that you can smell your cake without
// consuming it. Change the code above to pass in a &party. Then uncomment and run the code
// below. After all, you want to smell your cake and eat it, too!
// println!("Yum! I'm eating this cake: {:?}. Oops, I dropped it on the floor.", party.cake);
// drop(cake);
println!(
"Yum! I'm eating this cake: {:?}. Oops, I dropped it on the floor.",
party.cake
);
}
pub fn admire_cake(cake: Cake) {
println!("What a nice {:?} cake! 🎂", cake);
}
// pub fn smell_cake<T: Into<Cake>>(something: T) {
// println!("Hmm...something smells like a {:?} cake!", something.into());
// }
pub fn smell_cake<T: Into<Cake>>(something: T) {
println!("Hmm...something smells like a {:?} cake!", something.into());
}