最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

rust - How can I know if this piece of code is thread safe or not? - Stack Overflow

programmeradmin4浏览0评论

I've written a piece of code that aims to encode into and decode base64 characters. So far, my code is working, but I've been made aware of the risk of a data race in a previous code of mine, and I believe that is because I've been using mut variables inside a par_iter()... block.

I'm asking this question to see if my belief is right. If so, what is the best way to prevent this?

Here is the exact snippet from my code that I think could lead to a race condition.

let i: Vec<u8> = h.into_par_iter().map(|f| {
    let l = if f + 8 > e.len() {
        e.len()
    } else {
        f + 8
    };
    //Citations: .html
    let st = &e[f..l];
    let mut j = 0u8;

    for (i, ch) in st.chars().enumerate() {
        if ch == '1' {
            j |= 1 << (7 - i);
        }
    }
    
    j
}).collect();

I've written a piece of code that aims to encode into and decode base64 characters. So far, my code is working, but I've been made aware of the risk of a data race in a previous code of mine, and I believe that is because I've been using mut variables inside a par_iter()... block.

I'm asking this question to see if my belief is right. If so, what is the best way to prevent this?

Here is the exact snippet from my code that I think could lead to a race condition.

let i: Vec<u8> = h.into_par_iter().map(|f| {
    let l = if f + 8 > e.len() {
        e.len()
    } else {
        f + 8
    };
    //Citations: https://doc.rust-lang./book/ch04-03-slices.html
    let st = &e[f..l];
    let mut j = 0u8;

    for (i, ch) in st.chars().enumerate() {
        if ch == '1' {
            j |= 1 << (7 - i);
        }
    }
    
    j
}).collect();
Share Improve this question edited Mar 17 at 7:40 Dave Shah asked Mar 17 at 6:32 Dave ShahDave Shah 657 bronze badges 4
  • Thread safety issues can arise only when you have threads and write to shared data. With Rust it is difficult to even do that by default. So what makes you think it is even an issue here? – freakish Commented Mar 17 at 6:49
  • Well since there is a mutable variable, j, and I'm using par_iter, I assume there's gonna be more than 1 thread accessing at the same time, and unless I'm wrong, I believe that if more than one of the parallel threads are accessing j at a given time, the result could be altered due to the race condition that follows – Dave Shah Commented Mar 17 at 6:57
  • 1 Why do you assume that there will be more than one thread accessing it? That's first question. j cannot be accessed by multiple threads, it is not shared at all, it is a local variable, each thread has its own copy. The second question is: why do you think that if some variable is mut then there's some safety issue? Rust, unlike other languages, has a built in mechanism that prevents sharing mutable instances (the borrow checker). So you are safe by default, unless you do unsafe Rust. – freakish Commented Mar 17 at 7:26
  • The basic guarantee you have to understand with Rust is that Rust has 0% undefined behavior (given you don't use the unsafe keyword). The design of the language itself guarantees that, combined with a pessimistic compiler (refuses to compile if it cannot guarantee that there is no undefined behavior). So if you are afraid of something like multiple threads accessing a variable mutably at the same time, that's undefined behavior and is impossible as long as you do not use the unsafe keyword. – Finomnis Commented Mar 17 at 21:05
Add a comment  | 

1 Answer 1

Reset to default 6

Due to the existence of Send and Sync, as well as the borrow checker, it should be impossible to cause a data race in Rust without the use of unsafe.

In this particular case, no, there is no issue. This is because the mut variable is created inside the closure, not captured from outside of it. While the closure value is shared between all threads involved in the parallel operation, each execution of the closure creates its own stack frame on the executing thread with its own variables. Therefore, each call creates an independent set of function-local variables, including j.

This is how normal functions work, too -- a function that declares a local mut variable is not inherently thread-unsafe because each execution gets its own instance of the local variables.

If you were to capture a mut variable, you would not be able to modify it from a closure passed to .map on a parallel iterator, because that closure must implement Fn. You cannot mutate your captured environment from a Fn, only from a FnMut.

This example demonstrates the situation you are worried about (mutating shared state from multiple threads) and how Rust disallows it. Note that v is declared outside of the closure, not inside -- this means all instances of the closure share a single instance of v.

use rayon::prelude::*;

fn main() {
    let mut v = 0;

    [0].into_par_iter().map(|x| {
        v += 1;
        x
    });
}
error[E0594]: cannot assign to `v`, as it is a captured variable in a `Fn` closure
 --> src/main.rs:7:9
  |
7 |         v += 1;
  |         ^^^^^^ cannot assign
发布评论

评论列表(0)

  1. 暂无评论