Giter Site home page Giter Site logo

100-exercises-to-learn-rust's Introduction

Learn Rust, one exercise at a time

You've heard about Rust, but you never had the chance to try it out?
This course is for you!

You'll learn Rust by solving 100 exercises.
You'll go from knowing nothing about Rust to being able to start writing your own programs, one exercise at a time.

Note

This course has been written by Mainmatter.
It's one of the trainings in our portfolio of Rust workshops.
Check out our landing page if you're looking for Rust consulting or training!

Getting started

Go to rust-exercises.com and follow the instructions there to get started with the course.

Requirements

  • Rust (follow instructions here).
    If rustup is already installed on your system, run rustup update (or another appropriate command depending on how you installed Rust on your system) to make sure you're running on the latest stable version.
  • (Optional but recommended) An IDE with Rust autocompletion support. We recommend one of the following:

Solutions

You can find the solutions to the exercises in the solutions branch of this repository.

License

Copyright Β© 2024- Mainmatter GmbH (https://mainmatter.com), released under the Creative Commons Attribution-NonCommercial 4.0 International license.

100-exercises-to-learn-rust's People

Contributors

alphacodezero avatar c-git avatar datewu avatar ehershey avatar eugenefil avatar fangyi-zhou avatar felixpherry avatar jaywonchung avatar jrycw avatar jw013 avatar keshav-c avatar lukemathwalker avatar metajack avatar morinokami avatar palash25 avatar phmx avatar rithvik-bosch avatar saqib-ahmed avatar sh099078 avatar sonathad avatar sympatron avatar tomgrbz avatar vcheckzen avatar vrnvu avatar woju avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

100-exercises-to-learn-rust's Issues

Question: practical reason for references to integral types?

I just finished 03_ticket_v1 and was wondering why you would want to return a reference to an integral type? Because a reference is usize, I can understand why you wouldn't want to copy types where std::mem::size_of<T>() > std::mem::size_of<usize>. Is it just to reinforce the notion that we are returning a reference to a struct member? Or are there other practical reasons?

Also, if there is a better venue for asking these sorts of questions, I'd be happy to utilize that in the future.

Thank you for putting this together! I wish I would have had this when I started playing with Rust 3 or 4 years ago!

4.9: Flesh out the explanation of `Output`

I think 4.9 could benefit from an additional paragraph expanding on what type Output; does.
In the solution for "4.13. Outro", there is the line type Output = SaturatingU16;, which I think could be type Output = Self;, since it is implemented for SaturatingU16?

I guess my main point of confusion is, "Why do we need to create the Output type, when we can just specify the return type in the function signature below?" and "Why can't we just specify -> Self in the function signature, why do we need -> Self::Output?

Visualize modules graphically via the `cargo-modules` crate

Description

I am currently working on the 03_modules exercise and I found out about this crate cargo-modules which is quite nice. Perhaps it can be suggested as an optional resource?

Below, a usage example πŸ‘‡πŸΌ

❯ cargo modules structure --package modules

crate modules
β”œβ”€β”€ struct Ticket: pub(crate)
β”‚   └── fn new: pub(crate)
└── mod helpers: pub(crate)
    └── fn create_todo_ticket: pub(self)

cargo_modules

Suggestion for improvement in instruction on 03_07_setters

Hello, and thanks for writing a really great resource! I'm really having fun learning Rust with this!

I had to check the solution for this problem, since I was trying to create a method on the struct for validating title, description and status. But in the solution, I see that it is a private function, not method that is created. The instruction had me trying to add the validate functions within the impl Ticket block.

So, unless I have misunderstood methods vs functions (which is entirely possible!) I suggest replacing the word methods with functions on the line I linked above.

Question about book 3.7 setter

As a Rust beginner it's nice to come across such great tutorials, thanks to the contributors.

I had an issue while reading section 3.7: link

&mut-setters have a downside: you can't chain multiple calls together. Since they don't return the modified Ticket instance, you can't call another setter on the result of the first one. You have to call each setter separately:

I was curious if it was possible to chain calls by returning &mut Self, and after trying it seems to work.
playground link
gist link

So I'm not sure if there are downsides to this implementation that lead to this description in the book?

workshop-runner might require update

Hello,

I pulled the repo mid-May before 04_traits/05_trait_bounds was added, I have now pulled the latest changes, but the wr command seems to still seek 04_traits/05_str_slice which doesn't exist anymore.

I'm unsure if the folder structure was hardcoded in the runner or if I need to do something on my end to have it recognize the new exercise order (I tried uninstalling the crate and reinstalling)

image

error: manifest path exercises/04_traits/05_str_slice/Cargo.toml does not exist

Thank you!

Test case's name implies mutable reference but a regular reference is present - (03) ticket_v1 - (10) references_in_memory

Test case u64_mut_ref_size implies that the reference whose size is being tested is mutable, but the reference in the actual test case is immutable, just like in two other test cases which do not contain the word mut in their names.

If this exercise is supposed to test whether somebody understands that immutable and mutable references have the same size in memory, then the type in that test case should be &mut u64.

#[cfg(test)]
mod tests {
    use super::Ticket;
    use std::mem::size_of;

    #[test]
    fn u16_ref_size() {
        assert_eq!(size_of::<&u16>(), todo!());
    }

    #[test]
    fn u64_mut_ref_size() {
        assert_eq!(size_of::<&u64>(), todo!());
    }

    #[test]
    fn ticket_ref_size() {
        assert_eq!(size_of::<&Ticket>(), todo!());
    }
}

updated my local clone from main, now I'm stuck in an non-existent exercise

I had unlocked an old exercice, chapter 4, ex 5 about string slices.

But by following the online book I noted that ex 5 is now about trait bounds. So I decided to update from origin and rebase my way back to where I was.

Now wr is stuck on a chapter 5 about string slices which doesn't exists, it's now chapter 6. How can I solve this?

	❌ (04) traits - (05) str_slice
error: manifest path `exercises/04_traits/05_str_slice/Cargo.toml` does not exist

Questions about (05) ticket_v2 - (13) try_from

In this task, I've completed the part where I add both features to the Status enum. But after watching (https://www.youtube.com/watch?v=b0bgAYJDhhQ) i'm tempted to try out the code below:

impl TryFrom<Into<String>> for Status {
    type Error = ParseStatusError;

    fn try_from(value: impl Into<String>) -> Result<Self, Self::Error> {
        let value: String = value.into();
        match value.to_lowercase().as_str() {
            "todo" => Ok(Status::ToDo),
            "inprogress" => Ok(Status::InProgress),
            "done" => Ok(Status::Done),
            _ => unimplemented!(),
        }
    }
}

I ran into problems like "Into can't become an object" and some others.

I also tried pub trait MyTrait: Into<String> + Sized {} (because https://doc.rust-lang.org/std/convert/trait.TryFrom.html requires a subtrait Sized). But it looks like doing it the way I wanted is not possible.

Now i'm wondering, I'm not sure if it's just my approach that's wrong or it's just impossible.
Any insights and suggestions would be greatly appreciated.

Questions about purpose of solutions for (04) traits - 14 (outro)

Hi, I'm trying to understand something about the solutions code. Can someone explain what is the purpose of the piece of code below or why is it required? I'm confused because even when I remove it, the tests still pass. All I know is that it's related to fixing a warning about something called op_ref on the last assert_eq!.

impl std::ops::Add<&SaturatingU16> for SaturatingU16 {
    type Output = SaturatingU16;

    fn add(self, rhs: &SaturatingU16) -> Self::Output {
        self + *rhs
    }
}

Any insights would be much appreciated! Thanks in advance!

Faulty solution for Chapter 06 Ticket Management, 13 Index

The solution for implementing Index<&TicketId> is written like this:

impl Index<&TicketId> for TicketStore {
    type Output = Ticket;

    fn index(&self, id: &TicketId) -> &Self::Output {
        &self[*id]
    }
}

Here self refers to the TicketStore {counter: u64, tickets: Vec}. So the index function should not work.

The fix should be to change it to

impl Index<&TicketId> for TicketStore {
    type Output = Ticket;

    fn index(&self, id: &TicketId) -> &Self::Output {
        self.tickets.iter().find(|t| t.id == *id).unwrap()
    }
}

which is the same implementation as Index

edits: fix code formatting

04_traits/09_from Into<&str> for String

In the section "&str to String" there is a paragraph:

We've been primarily using .into(), though.
If you check out the implementors of Into you won't find Into<&str> for String. What's going on?

Is Into<&str> for String really what we're looking for? That converts String into &str. But, according to the section header, we want the opposite - hypothetical Into<String> for &str. No?

How to run the gitbook locally like the rust book?

Hi, First off thank you so much for this book. I was thinking, now that I have cloned this repository, is there any way to serve this git book locally? Like for example how you can open the rust book using rustup doc --book.

05/10 Unclear objectives

I found it impossible to guess what structure was requested, and difficult to parse what the result actually means.

There's a lib.rs, so I thought to make a pub(crate) hello_world() there. (Wrong, IIUC because we're using the crate as though external, so it needs to be just pub)

Next, I thought, OK, maybe we're making a new interior crate? But the text refers to cargo new, not cargo init, or creating any directories.

At this point, I'm just confused so I look at the solution.

07_threads/12_rw_lock question

Hi! Just finished the exercise by find and replacing Mutex with RwLock, which compiled and ran.

Solution seems to indicate that was the move as well.

Shouldn't there be something in the exercise that forces you to use read and write, or is that not possible to check via the tests?

Feedback: (06) ticket_management - (02) vec could lead to a much more complex problem

Hi, thanks for the exercices, I enjoy them.

In exercice (06) ticket_management - (02) vec, you have to complete the following function:

pub fn fibonacci(n: u32) -> u32 {
    // TODO: implement the `fibonacci` function
    //
    // Hint: use a `Vec` to memoize the results you have already calculated
    // so that you don't have to recalculate them several times.
    todo!()
}

Following the hint, I used a vec to memoize the results in a vec, but my code ended up a tiny bit too complex with some once_cell::sync::Lazy and std::sync::Mutex, which doesn't seem right for such an exercice. I have done memoization in Rust before, and it's not super fun, so that could explain why I used once_cell stuff, but to me the hint was about caching the results between calls.

use once_cell::sync::Lazy;
use std::sync::Mutex;

static FIBONACCI_MEMO: Lazy<Mutex<Vec<u32>>> = Lazy::new(|| Mutex::new(vec![0, 1]));

pub fn fibonacci(n: u32) -> u32 {
    let nusize = n as usize;
    let mut memo = FIBONACCI_MEMO.lock().unwrap();

    if memo.len() <= nusize {
        if memo.capacity() <= nusize {
            memo.reserve(nusize * 2);
        }
        std::mem::drop(memo);
        let result = fibonacci(n - 2) + fibonacci(n - 1);
        let mut memo = FIBONACCI_MEMO.lock().unwrap();
        memo.push(result);
        return result;
    }

    memo[nusize]
}

I looked at the solution and IΒ see that it's much simpler.

pub fn fibonacci(n: u32) -> u32 {
    let n = n as usize;
    let mut memo = vec![0, 1];
    for i in 2..=n {
        memo.push(memo[i - 1] + memo[i - 2]);
    }
    memo[n]
}

But I'm not sure the Vec is necessary at all:

pub fn fibonacci(n: u32) -> u32 {
    if n <= 1 {
        return n;
    }

    let mut a = 0;
    let mut b = 1;

    for _ in 2..=n {
        let c = a + b;
        a = b;
        b = c;
    }

    b
}

(07) threads - (05) channels will not pass

Exercise (07) threads - (05) channels won't pass for me even when using the solution in the solutions branch.

Error:

        ❌ (07) threads - (05) channels

        Meditate on your approach and return. Mountains are merely mountains.



error: test failed, to rerun pass `--test insert`

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s


running 2 tests
ready --- FAILED
.
failures:

---- ready stdout ----
thread 'ready' panicked at exercises/07_threads/05_channels/tests/insert.rs:31:5:
assertion failed: move_forward
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace


failures:
    ready

test result: FAILED. 1 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.20s

Error with RUST_BACKTRACE=1:

        ❌ (07) threads - (05) channels

        Meditate on your approach and return. Mountains are merely mountains.



error: test failed, to rerun pass `--test insert`

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s


running 2 tests
ready --- FAILED
.
failures:

---- ready stdout ----
thread 'ready' panicked at exercises/07_threads/05_channels/tests/insert.rs:31:5:
assertion failed: move_forward
stack backtrace:
   0: rust_begin_unwind
             at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/std/src/panicking.rs:645:5
   1: core::panicking::panic_fmt
             at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/core/src/panicking.rs:72:14
   2: core::panicking::panic
             at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/core/src/panicking.rs:145:5
   3: insert::ready
             at ./tests/insert.rs:31:5
   4: insert::ready::{{closure}}
             at ./tests/insert.rs:22:11
   5: core::ops::function::FnOnce::call_once
             at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/core/src/ops/function.rs:250:5
   6: core::ops::function::FnOnce::call_once
             at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/core/src/ops/function.rs:250:5
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.


failures:
    ready

test result: FAILED. 1 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.20

ch02 ex10 `u8_to_i8` test improvement

With the test being annotated with

#[allow(overflowing_literals)]

in combination with the test being:

assert_eq!(255 as i8, todo!());

a naΓ―ve answer of 255_i8 succeeds.

I'm guessing the idea of this is that one should answer with -1_uX but the person I was guiding through the exercises would have skipped right by if I hadn't said "this assertion will fail".

Maybe also relevant is that the overflow-checks profile, set on the workspace (as is necessary) from a couple of exercises ago is likely still there. If so, this set of tests might need one that "asserts" we turned it off again. (Though I suppose that interferes with the re-runnability of the tests.)

Clarify *which* `Cargo` file should be modified in Basic Calculator/2.8 (Overflow)

I'm loving this so far, but one issue I just ran into that made me look at the solutions was that modifying the Cargo.toml under exercises/02_basic_calculator/08_overflow wouldn't work when running wr, and you had to modify the root level Cargo.toml instead to get the expected overflow behavior.

As a beginner to Rust I was quite confused as to why this was failing/not working, so maybe specifying/clarifying how Cargo works exactly would be good before having people start modifying it? I had presumed that it would automatically pick up whatever is the closest Cargo.toml to the tests, but apparently that isn't the case.

For example, it would be nice to understand why it is that every single exercise has its own Cargo.toml, especially if it doesn't seem like these Cargo files are being respected? This might be covered in a latter exercise, in which case maybe introducing it earlier would be good?

This could also be a setup/local issue but I doubt it, as it's a fresh Rust install and I just opened the entire repo/folder in RustRover.

Interior mutability exercice question

So basically on the drop method i had to put this code :

fn drop(&mut self) {
    *(*self.counter).borrow_mut() += 1;                                       
} 

Instead of this :

fn drop(&mut self) {
    *self.counter.borrow_mut() += 1;                                   
} 

When trying the latter i was getting this error :

cannot use += on type Rc<RefCell<usize>>

Which make sense because Rc implements BorrowMut that return a mut reference to itself.
So when we call self.counter.borrow_mut() it returns a &mut Rc and then when we dereference we just get Rc so "+=" don't work.

You can tell me if i'm the only one that had issues using the solution on the Interior Mutability exercice.

Wrong requirements in chapter 3 task 2

In chapter 3 (ticket v1) task 2 (validation) you say the following:

    //   - the `title` should be at most 50 bytes long.
    //   - the `description` should be at most 500 bytes long.

The tests on the other hand state the following:

    #[should_panic(expected = "Title cannot be longer than 50 characters")]
    fn title_cannot_be_longer_than_fifty_chars() {
        Ticket::new(overly_long_title(), valid_description(), "To-Do".into());
    }

    #[should_panic(expected = "Description cannot be longer than 500 characters")]
    fn description_cannot_be_longer_than_500_chars() {
        Ticket::new(valid_title(), overly_long_description(), "To-Do".into());
    }

This would be incorrect, as utf-8 characters can be more than one byte in size. (think À,ß, etc.)

03_ticket_v1/05_encapsulation all private fields vs any private field

Hi! Learning rust with your course. Thanks a lot!

Shouldn't the following

If all fields are private, it is no longer possible to create a `Ticket` instance directly...

be

If __any field is__ private, it is no longer possible to create a `Ticket` instance directly...

meaning it's enough to have a single private field and you can't instantiate directly.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.