Commit Graph

21 Commits

Author SHA1 Message Date
Matthias Krüger
aec535f805 Rollup merge of #94559 - m-ou-se:thread-scope-spawn-closure-without-arg, r=Mark-Simulacrum
Remove argument from closure in thread::Scope::spawn.

This implements ```@danielhenrymantilla's``` [suggestion](https://github.com/rust-lang/rust/issues/93203#issuecomment-1040798286) for improving the scoped threads interface.

Summary:

The `Scope` type gets an extra lifetime argument, which represents basically its own lifetime that will be used in `&'scope Scope<'scope, 'env>`:

```diff
- pub struct Scope<'env> { .. };
+ pub struct Scope<'scope, 'env: 'scope> { .. }

  pub fn scope<'env, F, T>(f: F) -> T
  where
-     F: FnOnce(&Scope<'env>) -> T;
+     F: for<'scope> FnOnce(&'scope Scope<'scope, 'env>) -> T;
```

This simplifies the `spawn` function, which now no longer passes an argument to the closure you give it, and now uses the `'scope` lifetime for everything:

```diff
-     pub fn spawn<'scope, F, T>(&'scope self, f: F) -> ScopedJoinHandle<'scope, T>
+     pub fn spawn<F, T>(&'scope self, f: F) -> ScopedJoinHandle<'scope, T>
      where
-         F: FnOnce(&Scope<'env>) -> T + Send + 'env,
+         F: FnOnce() -> T + Send + 'scope,
-         T: Send + 'env;
+         T: Send + 'scope;
```

The only difference the user will notice, is that their closure now takes no arguments anymore, even when spawning threads from spawned threads:

```diff
  thread::scope(|s| {
-     s.spawn(|_| {
+     s.spawn(|| {
          ...
      });
-     s.spawn(|s| {
+     s.spawn(|| {
          ...
-         s.spawn(|_| ...);
+         s.spawn(|| ...);
      });
  });
```

<details><summary>And, as a bonus, errors get <em>slightly</em> better because now any lifetime issues point to the outermost <code>s</code> (since there is only one <code>s</code>), rather than the innermost <code>s</code>, making it clear that the lifetime lasts for the entire <code>thread::scope</code>.

</summary>

```diff
  error[E0373]: closure may outlive the current function, but it borrows `a`, which is owned by the current function
   --> src/main.rs:9:21
    |
- 7 |         s.spawn(|s| {
-   |                  - has type `&Scope<'1>`
+ 6 |     thread::scope(|s| {
+   |                    - lifetime `'1` appears in the type of `s`
  9 |             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:9:13
    |
  9 |             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
    |
  9 |             s.spawn(move || println!("{:?}", a)); // might run after `a` is dropped
    |                     ++++
"
```
</details>

The downside is that the signature of `scope` and `Scope` gets slightly more complex, but in most cases the user wouldn't need to write those, as they just use the argument provided by `thread::scope` without having to name its type.

Another downside is that this does not work nicely in Rust 2015 and Rust 2018, since in those editions, `s` would be captured by reference and not by copy. In those editions, the user would need to use `move ||` to capture `s` by copy. (Which is what the compiler suggests in the error.)
2022-03-08 11:04:51 +01:00
Mara Bos
a3d269e91c Use f instead of || f().
Co-authored-by: Mark Rousskov <mark.simulacrum@gmail.com>
2022-03-07 22:14:02 +00:00
Mara Bos
9099353ea8 Use '_ for irrelevant lifetimes in Debug impl.
Co-authored-by: Daniel Henry-Mantilla <daniel.henry.mantilla@gmail.com>
2022-03-04 10:41:39 +00:00
Mara Bos
24fe35a3e1 Remove argument from closure in thread::Scope::spawn. 2022-03-03 13:04:14 +01:00
Mara Bos
af86b55735 Remove unnecessary #![feature]s from doctest. 2022-03-03 12:09:18 +01:00
Mara Bos
dadf2adbe9 Rename JoinHandle::is_running to is_finished and update docs.
Co-authored-by: Josh Triplett <josh@joshtriplett.org>
2022-03-03 12:09:17 +01:00
Mara Bos
465c405418 Add test for thread::Scope invariance. 2022-01-22 17:15:08 +01:00
Mara Bos
4cb73704e2 Mention *scoped* thread in panic message.
Co-authored-by: Amanieu d'Antras <amanieu@gmail.com>
2022-01-05 11:17:11 +00:00
Mara Bos
aa9c0881ef Note the invariance over 'env in Scope<'env>. 2022-01-05 12:14:32 +01:00
Mara Bos
5bd5781823 Fix missing .load() in Scope's Debug impl. 2022-01-05 12:14:32 +01:00
Mara Bos
a9efbaf3a5 Rename n_running_threads to num_running_threads. 2022-01-05 12:14:32 +01:00
Mara Bos
5b5746f081 Fix typo in Scope::spawn docs.
Co-authored-by: deltragon <m@dafert.at>
2022-01-04 18:43:23 +00:00
Mara Bos
c429ade760 Fix typo in is_running() docs.
Co-authored-by: Mattias Buelens <649348+MattiasBuelens@users.noreply.github.com>
2022-01-04 18:40:00 +01:00
Mara Bos
09e6665aba Fix typo in documentation. 2022-01-04 17:05:26 +01:00
Mara Bos
2c8cc70ea0 Use > rather than == for overflow check in scoped threads.
Co-authored-by: Jacob Lifshay <programmerjake@gmail.com>
2022-01-04 15:58:29 +00:00
Mara Bos
c5cb2def06 Fix variance of thread::Scope. 2022-01-04 16:57:16 +01:00
Mara Bos
4300bea0c2 Formatting. 2022-01-04 16:32:39 +01:00
Mara Bos
f5217792ed Simplify panicking mechanism of thread::scope.
It now panic!()s on its own, rather than resume_unwind'ing the panic
payload from the thread. Using resume_unwind skips the panic_handler,
meaning that the main thread would never have a panic handler run, which
can get confusing.
2022-01-04 16:10:14 +01:00
Mara Bos
da33da161b Add documentation for scoped threads. 2022-01-04 16:09:53 +01:00
Mara Bos
cc699e1b62 Add ScopedJoinHandle::is_running(). 2022-01-04 15:15:41 +01:00
Mara Bos
0e24ad537b Implement RFC 3151: Scoped threads. 2022-01-04 14:51:39 +01:00