Should We Learn All Syntax and Async Rust?

Hello everyone,

I’ve recently started my journey learning Rust. Since my English is at a beginner level, I am currently following a translation of the official Rust book in my native language. I’ve been wanting to learn this language for about 3 years.

The resource I’m using covers almost every topic in detail, but it doesn’t touch on 'Async Rust'. My goals are entirely hobby-oriented rather than commercial. Specifically, I want to work on compiler writing, game development, CLI tools, and desktop applications.

In this context, I have the following questions:

  1. Is it mandatory for me to learn Async Rust? Will I hit a roadblock if I don't?
  2. Does Rust support async natively (in the language itself), or is it entirely dependent on external libraries (runtimes)?
  3. Does the compiler really help us that much?

Additionally, I am currently working on a simple to-do project. Do you have any advice for a beginner?

use std::fmt::{self, Formatter, format, write};

#[derive(Debug)]
struct Title(String);

#[derive(Debug)]
struct Desc(String);

#[derive(Debug)]
enum Status {
    TODO,
    INPROGRESS,
    DONE,
}

#[derive(Debug)]
struct Todo {
    title: Title,
    desc: Desc,
    done: Status,
}

impl Todo {
    fn new(title: Title, desc: Desc, done: Option<Status>) -> Self {
        Self {
            title,
            desc,
            done: done.unwrap_or(Status::INPROGRESS),
        }
    }
}

impl std::fmt::Display for Todo {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        match self.done {
            Status::TODO => write!(f, "[TODO] - {}\n {}", self.title.0, self.desc.0),
            Status::INPROGRESS => write!(f, "[INPROGRESS] - {}\n {}", self.title.0, self.desc.0),
            Status::DONE => write!(f, "[DONE] - {}\n {}", self.title.0, self.desc.0),
        }
    }
}

impl Title {
    fn new(title: String) -> Result<Self, String> {
        if title.is_empty() {
            return Err(String::from("The Title field cannot be empty."));
        }

        Ok(Self(title))
    }
}

impl Desc {
    fn new(desc: String) -> Result<Self, String> {
        if desc.is_empty() {
            return Err(String::from("The Description field cannot be empty."));
        }

        Ok(Self(desc))
    }
}

struct TodoList {
    todos: Vec<Todo>,
}

impl TodoList {
    fn new() -> Self {
        Self { todos: Vec::new() }
    }

    fn push(&mut self, todo: Todo) -> () {
        self.todos.push(todo);
    }

    fn list(&self) {
        self.todos.iter().for_each(|todo| println!("{}", todo));
    }
}

fn main() -> Result<(), String> {
    let mut todo_list = TodoList::new();

    let my_todo: Todo = Todo::new(
        Title::new(String::from("kaan"))?,
        Desc::new(String::from("kaan"))?,
        None,
    );

    let my_todo_2: Todo = Todo::new(
        Title::new(String::from("Kaan"))?,
        Desc::new(String::from("kaan"))?,
        Some(Status::TODO),
    );

    todo_list.push(my_todo);
    todo_list.push(my_todo_2);
    todo_list.list();

    Ok(())
}

Thank you.

For your first question, It depends entirely on what you want to do first. I've looked a little into it when I learned Rust more than 3 years ago, but I've never had to use it in my projects, which are mostly focused on compiler tools. But game development and desktop applications might indeed need it. It seems to be a powerful tool in the Godot framework for everything event-related.

I'm also interested in what developers who know more about it think about the second question.

I have the impression there's currently a split in Rust. For example, when I learned about it[1], the examples used async_std, which has now been replaced by smol. There seems to be several alternatives, Tokio being a popular one. So, if I understand the situation correctly, and even if there's some common ground, I wonder if the best isn't simply to start learning about it in the context of your project: does it need Tokio? Is smol enough? Or Godot's own framework?

All that to say, I'm happy I haven't spent too much time learning something that's changing. It seems a lot of work is being done, but again, it's best to hear it from someone who's actually using it, as I might have got it wrong.


  1. I learned about it in Programming Rust (2nd edition), which did cover the topic adequately at the time. Hopefully the 3rd edition, due in July, will be updated with the recent changes. ↩︎

2 Likes

You probably do not need to learn async Rust, it is a rather obscure (and tricky) part of the language.

It is part of the language though, async is a keyword, also await.

1 Like

Going into gamedev is probably the most likely place you'll encounter async, as it's the most likely place you'll use 3rd party libraries that use it. However, I think you could definitely avoid async if you don't want to learn it.

Rust doesn't have a built-in runtime, instead you must pick and choose one from an external library (or write your own). The downside to this is that the ecosystem is quite fractured: if your code uses Tokio, you can't really use libraries written for async-std or smol, and vice-versa. Of course, Rust has very good reasons for not bundling an executor.

Async really does divide the user base. To most people working on anything web-related (which is most programmers in general, although not most Rust programmers), async is the natural and obvious way to do anything.

For desktop applications not touching the internet much, where latencies are lower but you still need to keep the UI responsive, async/await can be a nice way to abstract over concurrency, or you can just choose to use background threads and synchronization the old-fashioned way.

For command-line tools and anything working in a "batch mode" rather than interactively, async is often nigh useless except if the tool wants to do a lot of concurrent I/O of some sort, for example processing multiple files or making multiple network requests.

For game development, async/await is a very useful tool in writing "scripts", i.e. the logic for how various game objects behave. It can also be useful for writing code to stream asset data (e.g. textures, 3D models, etc.) from the disk in the background, to minimize the time the player spends staring at loading screens.

3 Likes

Is it mandatory for me to learn Async Rust? Will I hit a roadblock if I don't?

It is not mandatory until you plan to use it. Lots of domains would never touch async, just like lots of domains never need to touch unsafe. Both are greatly useful when they're needed though.

You also will not understand async without thoroughly understanding non-async "normal" rust first. You shouldn't worry about async until you are very comfortable with the basics. Again, this is similar to unsafe in that regard.

Does Rust support async natively (in the language itself), or is it entirely dependent on external libraries (runtimes)?

Both. The semantics of async are built into the language, but to actually "run" the async code you need a runtime. There are multiple external libraries that offer runtimes and you can also even write one yourself. You are in control.

To answer more directly: Yes, you do need a runtime, and the standard library doesn't offer one.

Does the compiler really help us that much?
I'm not sure what this question is asking about specifically, so I will simply reply: Yes :slight_smile:

You can ask AI/LLM about the compiler's implementation of async functions.

It is interesting that "local variables" are preserved in an internal enum.

Each await (state) will create an additional entry in the enum.

So, yes, the Rust compiler does some heavy lifting. And Rust leaves the "driver" part to third-party libraries.