Chenyo's org-static-blog

Posts tagged "kaist":

08 Mar 2026

KAIST CS431 Notes

Notes for KAIST CS431: Concurrent Programming.

Lecture videos are here.

Some topics may be out of the course scope, but come up along this note-taking process.

1. thread::scope

fn increment_both_half(data: &mut [i32]) {
    let (left, right) = data.split_at_mut(data.len() / 2);

    thread::scope(|scope| {
        scope.spawn(|_| {
            for x in left {
                *x += 1;
            }
        });

        scope.spawn(|_| {
            for x in right {
                *x += 1;
            }
        });
    });

    println!(date);
}
  • Use thread::scope when the threads must borrow data owned by the current stack frame. Otherwise`thread::spawn` would fail as it requires 'static or clone.
  • The Scope is only returned when all threads are joined (and consequently blocks), ensuring threads cannot outlive the borrowed data

2. unsafe fn

pub struct NonZeroUsize(usize);

pub fn make_nonzero(x: usize) -> Option<NonZeroUsize> {
    if x == 0 {
        None
    } else {
        Some(NonZeroUsize(x))
    }
}

pub unsafe fn consume_nonzero(x: u32) -> NonZeroUsize {
    NonZeroUsize(x)
}
  • Mark a function as unsafe when calling it requires the caller to upload some invariants which the compiler cannot guarantee.
  • Whether a function is unsafe has nothing to do with its implementation, e.g., the API can be safe with unsafe code block.
  • Calling an unsafe fn must be wrapped with unsafe {} block.

3. object safety

  • Object safety is a set of rules whether a trait can be used for dynamic dispatch, i.e., dyn trait.
  • These rules are already listed in 2024-12-28-interesting-rust-snippets.html.
  • These rules ensure
    • A sized vtable can be built for each concrete implementation of the trait (rule 1), and
    • The caller can know the type size returned from any trait method call (rule 2).

4. async_trait

trait Data {
    fn fetch(&self) -> Pin<Box<dyn Future<Output = String> + Send>>;
}

// is equivalent to

#[async_trait]
trait Data {
    async fn fetch(&self) -> String;
}
  • async_trait is only necessary for dynamic dispatch. For static dispatch, one can just write async fn fetch() -> String.
  • Why Box<dyn Future> not impl Future: to know type size during compiler time.
  • Why Pin: to prevent the future being moved and self-reference is broken when the future changes state, e.g.,

    async fn fetch(&self) {
        let data = String::from("hello");
        let reference = &data; // reference to local variable is stored as absolute address
        some_async().await; // suspend here
        println!("{}", reference); // resume here, reference still needed
    }
    
Tags: study rust kaist
Other posts