🦀 post a lot about rust and rust gamedev
✍️ wrote https://sokoban.iolivia.me
🔥 blog at https://iolivia.me
🧵 weekly #rustlang threads every Thursday - subscribe here https://forms.gle/Tcm7cAkLt4NF9ZCZ9
Async is perfect for I/O-bound operations where you're waiting for external resources, use async for concurrency with I/O. Use threads for CPU parallelism!
Async is perfect for I/O-bound operations where you're waiting for external resources, use async for concurrency with I/O. Use threads for CPU parallelism!
Async code needs a runtime to execute. The runtime manages the state machines, decides when to poll futures, and handles task scheduling. A main function in itself can be sync, with a runtime inside, or you could use #[tokio::main] to achieve the same with less boilerplate.
Async code needs a runtime to execute. The runtime manages the state machines, decides when to poll futures, and handles task scheduling. A main function in itself can be sync, with a runtime inside, or you could use #[tokio::main] to achieve the same with less boilerplate.
This won't work because Rc doesn't implement Send, which means BadTranscript doesn't implement Send, which means we actually can't move this value between threads and use it as an output of the async task.
This won't work because Rc doesn't implement Send, which means BadTranscript doesn't implement Send, which means we actually can't move this value between threads and use it as an output of the async task.
What if we wanted to return a transcript object like this?
This works!
What if we wanted to return a transcript object like this?
This works!
When you write async fn, Rust creates a Future - a value that may not be ready now but will be later. Futures are lazy and do nothing until you await them.
Behind the scenes, Rust compiles your async function into a state machine that can be paused at each await and resumed later.
When you write async fn, Rust creates a Future - a value that may not be ready now but will be later. Futures are lazy and do nothing until you await them.
Behind the scenes, Rust compiles your async function into a state machine that can be paused at each await and resumed later.
This is a perfect example to use async to handle all transcript requests concurrently and optimise resources. While we wait for that, we can do other work from another task.
This is a perfect example to use async to handle all transcript requests concurrently and optimise resources. While we wait for that, we can do other work from another task.
Let's see what happens when 3 students request transcripts using traditional blocking code. Not only is everything sequential, so each student needs to wait for all transcripts to finish before getting theirs, but also if we are on the main thread we'd be freezing the UI
Let's see what happens when 3 students request transcripts using traditional blocking code. Not only is everything sequential, so each student needs to wait for all transcripts to finish before getting theirs, but also if we are on the main thread we'd be freezing the UI
Send, Sync, Future, tokio, Pin, I'm sure you've heard a lot of intimidating terminology when it comes to async Rust, so this thread is about explaining the basics in a simple way.
We'll build a transcript generator to understand async/await, Futures
🧵👇
Send, Sync, Future, tokio, Pin, I'm sure you've heard a lot of intimidating terminology when it comes to async Rust, so this thread is about explaining the basics in a simple way.
We'll build a transcript generator to understand async/await, Futures
🧵👇
- Match on specific error variants to handle different cases
- Access error fields programmatically (like student_id, course_id)
- Make informed decisions based on the error type
- Match on specific error variants to handle different cases
- Access error fields programmatically (like student_id, course_id)
- Make informed decisions based on the error type
Notice how each layer of context provides additional info about where in the call stack the error occurred.
Notice how each layer of context provides additional info about where in the call stack the error occurred.
Let's add new functionality that also validates the student name before adding them to the db. First, we need to import the Context trait and then use .context() to add context to the error returned from validate_name.
Let's add new functionality that also validates the student name before adding them to the db. First, we need to import the Context trait and then use .context() to add context to the error returned from validate_name.
Building on top of this, let's explore how anyhow can help improve our error handling from here. We first switch Result<Student, String> to anyhow::Result<Student> and use the anyhow::anyhow! macro to create errors with messages.
Building on top of this, let's explore how anyhow can help improve our error handling from here. We first switch Result<Student, String> to anyhow::Result<Student> and use the anyhow::anyhow! macro to create errors with messages.
Version 2 - Returning bool to indicate success/failure but no error details, we know what failed but not why
Version 3 - Using Result to return success values and error details, a good compromise and the rust default
Version 2 - Returning bool to indicate success/failure but no error details, we know what failed but not why
Version 3 - Using Result to return success values and error details, a good compromise and the rust default
Let's explore error handling approaches using a university database with Student and Course structs, and a Database holding students, courses and an enrollments HashMap.
Now let's explore 3 implementations for error handling:
V1 - Panic
V2 - Bool
V3 - Result
Let's explore error handling approaches using a university database with Student and Course structs, and a Database holding students, courses and an enrollments HashMap.
Now let's explore 3 implementations for error handling:
V1 - Panic
V2 - Bool
V3 - Result
Error handling in Rust can be a complex topic, especially since a lot of the patterns are different from other languages.
Let's explore some basic options and then go deeper into using anyhow and thiserror crates
🧵👇
Error handling in Rust can be a complex topic, especially since a lot of the patterns are different from other languages.
Let's explore some basic options and then go deeper into using anyhow and thiserror crates
🧵👇
For larger files, it's better to use a buffered reader to avoid loading the entire file into memory. Here's how you can do that. Notice we use the BufReader and BufRead trait.
For larger files, it's better to use a buffered reader to avoid loading the entire file into memory. Here's how you can do that. Notice we use the BufReader and BufRead trait.
After we saw how to create, write, and read files, let's see how to delete a file, pretty simple! We could also use the equivalent for directories with fs::remove_dir or fs::remove_dir_all.
After we saw how to create, write, and read files, let's see how to delete a file, pretty simple! We could also use the equivalent for directories with fs::remove_dir or fs::remove_dir_all.
Technically not part of the std::fs API but another common pattern is to read a file and then parse its contents into a structured format. Here's how we can read back the grades file. We could also use JSON/CSV+real parser, this is just a simple example.
Technically not part of the std::fs API but another common pattern is to read a file and then parse its contents into a structured format. Here's how we can read back the grades file. We could also use JSON/CSV+real parser, this is just a simple example.
Another common task is creating directories. You can use fs::create_dir_all to create a directory and all its parent components if they are missing.
Another common task is creating directories. You can use fs::create_dir_all to create a directory and all its parent components if they are missing.
We can use fs::read_to_string to read the entire contents of a file into a string. This is super handy for small files. Again, we handle the Result to manage errors gracefully.
We can use fs::read_to_string to read the entire contents of a file into a string. This is super handy for small files. Again, we handle the Result to manage errors gracefully.