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.
- EarlyBinder and instantiating parameters
- Early and Late Bound Parameter Implementation Nuances
- Region inference (NLL); and anonymous lifetime
Problems(quibbles)
- 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(|_|{})
}
- ’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.
- 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,
I guess:
- Call the
s.spawn(|| println!("{a:?}")); // might run after
ais 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