Is the inference about invariants of 'scope and 'env(std::\thread::Scope<'scope, 'env: 'scope>) in this article correct?

When I read the code of std::thread, confused about invariants of 'scope and 'env, but at the same I think it’s very exciting and appropriate. It gave me a deeper understanding of lifetime bound. In the meantime, I'm trying to speculate on the compiler's work to convince myself. But in the end, I can only think that I am quibbling. So, I can't be sure of its correct type.

I will write out my quibbles and hope to be corrected. excited, but frightened!

Thanks!!! The follow content is corrected???

I think there are some basic points.

  1. EarlyBinder and instantiating parameters
  2. Early and Late Bound Parameter Implementation Nuances
  3. Region inference (NLL); and anonymous lifetime

Problems(quibbles)

  1. invariants of 'env
use core::marker::PhantomData;

pub struct Scope<'scope, 'e: 'scope> {
    scope: PhantomData<&'scope mut &'scope ()>,
    env: PhantomData<&'e mut &'e ()>,
}

pub fn scope<'env, 'a, F, T>(f: F) -> T
    where
        'env: 'a,
        'a: 'env,  // when  env: PhantomData<&'env ()>, only it
        F: for<'scope> FnOnce(&'scope Scope<'scope, 'env>) -> T,
{
    let scope = Scope::<'_, 'a> {
        env: PhantomData,
        scope: PhantomData,
    };

    // let scope_f: &Scope<'_, 'env_f>` = &scope; //scope =>Scope<'_, 'a>;
    // f(scope_f)
    f(&scope)
}

fn main() {
    scope(|_|{})
}

(Playground)

  • ’env of the function scope is early bound. so scope(|_|{}) ==> scope::<'_main_env, 'main_a>(||{})
  • In the "fn scope<'env, 'a, F, T>(f: F) ", the f(&scope) ==>
let scope_f: &Scope<'_, 'env_f>` = &scope; //scope =>Scope<'_, 'a>;
f(scope_f)

thounght the signature of scope. 'env_f == 'env == 'main_env, Invariance over 'e of the sturct Scope, Scope<', 'a> must variance to &Scope<'_, 'env_f>`, SO

    where
        'env: 'a,
        'a: 'env,  // when  env: PhantomData<&'env ()>, only it

(when env: PhantomData<&'env ()>(the struct of Scope), only 'a: 'env, covariant over 'e of the sturct Scope)

The anonymous lifetime('_) is inferenced that it`s region maybe same of any named lifetime.

  1. invariants of 'scope
pub struct Scope<'scope, 'env: 'scope> {
    data: i32,
    /// Invariance over 'scope, to make sure 'scope cannot shrink,
    /// which is necessary for soundness.
    ///
    /// Without invariance, this would compile fine but be unsound:
    ///
    /// ```compile_fail,E0373
    /// std::thread::scope(|s| {
    ///     s.spawn(|| {
    ///         let a = String::from("abcd");
    ///         s.spawn(|| println!("{a:?}")); // might run after `a` is dropped
    ///     });
    /// });
    /// ```
    // scope: PhantomData<&'scope mut &'scope ()>,
    scope: PhantomData<&'scope ()>,
    env: PhantomData<&'env mut &'env ()>,
}

Why? (Without invariance, this would compile fine)

use std::marker::PhantomData;

pub struct Scope<'scope, 'env: 'scope> {
    /// Invariance over 'scope, to make sure 'scope cannot shrink,
    /// which is necessary for soundness.
    ///
    /// Without invariance, this would compile fine but be unsound:
    ///
    /// ```compile_fail,E0373
    /// std::thread::scope(|s| {
    ///     s.spawn(|| {
    ///         let a = String::from("abcd");
    ///         s.spawn(|| println!("{a:?}")); // might run after `a` is dropped
    ///     });
    /// });
    /// ```
    // scope: PhantomData<&'scope mut &'scope ()>,
    scope: PhantomData<&'scope ()>,
    env: PhantomData<&'env mut &'env ()>,
}

impl<'scope, 'env> Scope<'scope, 'env> {

    pub fn spawn<F, T>(&'scope self, f: F) -> T
        where
            F: FnOnce() -> T + Send + 'scope,
            T: Send + 'scope,
    {
        f()
    }
}

pub fn scope<'env, F, T>(f: F) -> T
    where
        F: for<'scope> FnOnce(&'scope Scope<'scope, 'env>) -> T,
{
    // We put the `ScopeData` into an `Arc` so that other threads can finish their
    // `decrement_num_running_threads` even after this function returns.
    let scope = Scope::<'_, '_> {
        env: PhantomData,
        scope: PhantomData,
    };

    f(&scope)
}

fn main() {
    // scope(|s| {
    //     s.spawn(|| {
    //         let a = String::from("abcd");
    //         s.spawn(|| println!("{a:?}")); // might run after `a` is dropped
    //     });
    // });

    scope(|s| {
        let a = String::from("abcd");
        s.spawn(|| println!("{a:?}")); // might run after `a` is dropped
        // Scope::spawn(s, || println!("{a:?}"));
    });
}

// pub fn spawn<F, T>(&'scope self, f: F) -> T
//     where
//         F: FnOnce() -> T + Send + 'scope,
//         T: Send + 'scope,

(Playground)

I guess:

  • Call the s.spawn(|| println!("{a:?}")); // might run after a is dropped at main.
let a = String::from("abcd");
let scope_spawn: &'sc111 Scope<'sc111, '_> = s; // &Scope<'scope, '_>
Scope<''sc111, '_>::spawn(scope_spawn)

if covariant over 'scope of the sturct Scope, sc111 < 'scope of s, the sc111 <= region of a, || println!("{a:?}") > sc111. The signature of Scope<''sc111, '_>::spawn allow sc111; So this would compile fine but be unsound.
if Invariance over 'scope of the sturct Scope. 'sc111 == 'scope outlive region of a, So 'sc111 > || println!("{a:?}"), Break F: FnOnce() -> T + Send + 'scope the signature of spawn. compile error.

Compiling playground v0.0.1 (/playground)
error[E0373]: closure may outlive the current function, but it borrows `a`, which is owned by the current function
  --> src/main.rs:57:17
   |
55 |     scope(|s| {
   |            - has type `&'1 Scope<'1, '_>`
56 |         let a = String::from("abcd");
57 |         s.spawn(|| println!("{a:?}")); // might run after `a` is dropped
   |                 ^^            - `a` is borrowed here
   |                 |
   |                 may outlive borrowed value `a`
   |
note: function requires argument type to outlive `'1`
  --> src/main.rs:57:9
   |
57 |         s.spawn(|| println!("{a:?}")); // might run after `a` is dropped
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
help: to force the closure to take ownership of `a` (and any other referenced variables), use the `move` keyword
   |
57 |         s.spawn(move || println!("{a:?}")); // might run after `a` is dropped
   |                 ++++

For more information about this error, try `rustc --explain E0373`.
error: could not compile `playground` (bin "playground") due to 1 previous error

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.