Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Stabilize `bind_by_move_pattern_guards` in Rust 1.39.0 #63118

Merged
merged 7 commits into from Sep 9, 2019

Conversation

@Centril
Copy link
Member

Centril commented Jul 29, 2019

Closes #15287.

After stabilizing #![feature(bind_by_move_pattern_guards)], you can now use bind-by-move bindings in patterns and take references to those bindings in if guards of match expressions. For example, the following now becomes legal:

fn main() {
    let array: Box<[u8; 4]> = Box::new([1, 2, 3, 4]);

    match array {
        nums
//      ---- `nums` is bound by move.
            if nums.iter().sum::<u8>() == 10
//                 ^------ `.iter()` implicitly takes a reference to `nums`.
        => {
            drop(nums);
//          --------- Legal as `nums` was bound by move and so we have ownership.
        }
        _ => unreachable!(),
    }
}

r? @matthewjasper

@Centril Centril added this to the 1.39 milestone Jul 29, 2019
Centril added a commit to Centril/rust that referenced this pull request Aug 1, 2019
…ewjasper

Make `#![feature(bind_by_move_pattern_guards)]` sound without `#[feature(nll)]`

Implements rust-lang#15287 (comment) making `#![feature(bind_by_move_pattern_guards)]]` sound without also having `#![feature(nll)]`. The logic here is that if we see a `match` guard, we will refuse to downgrade NLL errors to warnings. This is in preparation for hopefully stabilizing the former feature in rust-lang#63118.

As fall out from the implementation we also:
Fixes rust-lang#31287
Fixes rust-lang#27282

r? @matthewjasper
bors added a commit that referenced this pull request Aug 3, 2019
Make `#![feature(bind_by_move_pattern_guards)]` sound without `#[feature(nll)]`

Implements #15287 (comment) making `#![feature(bind_by_move_pattern_guards)]]` sound without also having `#![feature(nll)]`. The logic here is that if we see a `match` guard, we will refuse to downgrade NLL errors to warnings. This is in preparation for hopefully stabilizing the former feature in #63118.

As fall out from the implementation we also:
Fixes #31287
Fixes #27282

r? @matthewjasper
bors added a commit that referenced this pull request Aug 3, 2019
Make `#![feature(bind_by_move_pattern_guards)]` sound without `#[feature(nll)]`

Implements #15287 (comment) making `#![feature(bind_by_move_pattern_guards)]]` sound without also having `#![feature(nll)]`. The logic here is that if we see a `match` guard, we will refuse to downgrade NLL errors to warnings. This is in preparation for hopefully stabilizing the former feature in #63118.

As fall out from the implementation we also:
Fixes #31287
Fixes #27282

r? @matthewjasper
bors added a commit that referenced this pull request Aug 3, 2019
Make `#![feature(bind_by_move_pattern_guards)]` sound without `#[feature(nll)]`

Implements #15287 (comment) making `#![feature(bind_by_move_pattern_guards)]]` sound without also having `#![feature(nll)]`. The logic here is that if we see a `match` guard, we will refuse to downgrade NLL errors to warnings. This is in preparation for hopefully stabilizing the former feature in #63118.

As fall out from the implementation we also:
Fixes #31287
Fixes #27282

r? @matthewjasper
@Centril Centril force-pushed the Centril:stabilize-bind-by-move branch from d23bcda to 3193e31 Aug 4, 2019
@JohnCSimon

This comment was marked as resolved.

Copy link
Member

JohnCSimon commented Aug 17, 2019

Ping from triage
@Centril This one has sat idle for the last 13 days. Can you please post your status on this?

@Centril

This comment was marked as resolved.

Copy link
Member Author

Centril commented Aug 17, 2019

(This is waiting on me writing a report (the PR itself is done) and then FCP-merging that. It should be done by end of week.)

@Centril Centril force-pushed the Centril:stabilize-bind-by-move branch from 3193e31 to 4d11974 Aug 20, 2019
@Centril Centril changed the title [WIP] Stabilize `bind_by_move_pattern_guards` in Rust 1.39.0 Stabilize `bind_by_move_pattern_guards` in Rust 1.39.0 Aug 20, 2019
@Centril

This comment has been minimized.

Copy link
Member Author

Centril commented Aug 20, 2019

Stabilization proposal

I propose that we stabilize #![feature(bind_by_move_pattern_guards)].

@rfcbot merge

Tracking issue: #15287
Version target: 1.39 (2019-09-26 => beta, 2019-11-07 => stable).

What is stabilized

After stabilizing #![feature(bind_by_move_pattern_guards)], you can now use bind-by-move bindings in patterns and take references to those bindings in if guards of match expressions. For example, the following now becomes legal:

fn main() {
    let array: Box<[u8; 4]> = Box::new([1, 2, 3, 4]);

    match array {
        nums
//      ---- `nums` is bound by move.
            if nums.iter().sum::<u8>() == 10
//                 ^------ `.iter()` implicitly takes a reference to `nums`.
        => {
            drop(nums);
//          --------- Legal as `nums` was bound by move and so we have ownership.
        }
        _ => unreachable!(),
    }
}

Why?

Stabilizing #![feature(bind_by_move_pattern_guards)] has a few notable justifications:

  1. It allows you to continue using match guards when this is more natural. This is in particular useful when there is a catch-all arm which would need to be otherwise duplicated if the guards are moved into the match arm bodies using if expressions. Worse, the standard rustfmt style would move these if expressions into a block thereby adding rightward drift. This in turn harms readability.

  2. The checks made without #![feature(bind_by_move_pattern_guards)] are redundant as the NLL borrow checker already does the actual checks necessary for soundness. By removing the redundant checks, we can simplify the compiler and the language as seen in this PR.

Technical details

The following is lifted directly, in a slightly adjusted form, from @matthewjasper's technical report.

Bind by move guards

A by-value binding in a pattern with an if guard is lowered as follows:

  • Before evaluating the if guard, we create a binding by shared reference.
  • In the if guard, we use the shared reference when the binding is accessed.
  • If the if guard, evaluates to true then we do the expected binding.

For example, in:

let return_place = match scrutinee {
    binding if fun(&binding) => binding,
    ...
}

would lower to roughly:

let return_place;

let __binding_ref = &__scrutinee_tmp;
if !fun(&*__binding_ref)
    goto next_arm

let binding = __scrutinee_tmp;
return_place = binding;

Note: This lowering is observable in how we promote temporaries and how long different borrows can last.

Checking for mutation

To handle the cases in E0301 and E0302, "fake borrows" are added to the parts of the match scrutinee in the guards. More precisely: a shallow borrow is taken of any place that is:

  • Compared to a value, e.g. x.0 in match x { (false, _) if $expr }.
  • A reference / pointer / box that is dereferenced to access a compared value or a binding, e.g., x in match x { &(false, _) if $expr or match x { &v if $expr }.

The borrow lasts from the start of the guard to just after the guard is evaluated successfully. As such if a guard unconditionally diverges, then the borrow will not be active in the guard. A shallow borrow only affects a place and its parents. A shallow borrow of x.0 will prevent mutation of x and x.0, but not *(x.0) or x.0.f.

Tests

The tests can be primarily seen in the PR itself. Here are some of them:

History

  • On 2014-05-17, the idea of bind-by-move guards is raised in #14252.

  • On 2014-06-04, @zwarich proposes pattern guards with bind-by-move in rust-lang/rfcs#107 which is subsequently accepted on 2014-07-01.

  • On 2018-05-04, the RFC is "effectively implemented" by NLL as part of #49870 which was written @pnkfelix and reviewed by @nikomatsakis as well as @arielb1.

  • On 2018-09-18, the actual feature gate bind_by_move_pattern_guards as well as tests are added in #54034 which was written by @pnkfelix and reviewed by @nikomatsakis and @varkor.

  • On 2018-10-01, the -Z disable_ast_check_for_mutation_in_guard flag is removed and the feature gate is used in some tests instead in #54676 which was written by @pnkfelix and reviewed by @alexcrichton.

  • On 2019-01-03, the feature is discussed by the language team. @nikomatsakis expressed the wish for NLL progressing further on both editions before stabilizing the feature.

  • On 2019-05-02, the feature was again discussed by the language team. We noted that we should prepare the feature for stabilization and asked @matthewjasper to provide a report.

  • On 2019-05-04, @matthewjasper provides a report on the feature gate upon which the current stabilization report is based on. The report provides a motivation, an explanation, and highlights soundness holes without #![feature(nll)] a few tests which are lacking.

  • On 2019-08-04, the soundness holes, when using #![feature(bind_by_move_pattern_guards)] without also using #![feature(nll)], as noted by @matthewjasper are plugged in #63059 which was written by @Centril and reviewed by @matthewjasper. The soundness holes are fixed by avoiding to downgrade NLL errors as warnings in certain circumstances.

  • This PR stabilizes bind_by_move_pattern_guards.

Related future work

As @matthewjasper notes in the pre-report:

The following errors are also apparently unnecessary restrictions on patterns. There should probably be a check for why these are errors and whether they can be removed.

  • E0007 - cannot bind in a subpattern
  • E0009 - cannot bind by ref and by move
  • E0303 - cannot bind in a subpattern (again)

Apendix

How does the compiler detect the need for a bind-by-move?

Summary

An explanation of binding modes exists in the reference. The following summary is distilled from the algorithm below which is in turn a summary of the actual librustc_typeck source in fn check_pat_walk. Please note that what changes here is to allow taking references in if guards for existing by-move bindings. The algorithm for determining binding modes remains unchanged.

The default-binding-mode:

  • starts out as the one provided by the outer context or by-value if there is no outer context.
  • becomes by-value on a &mut? pattern.
  • becomes by-reference on a non_ref_pat.

A binding is:

  • by-reference when it is a ref mut? binding,
  • by-value when it is a mut binding,
  • by-the-default-binding-mode otherwise.

A by-value binding is by-copy if the type is Copy and by-move otherwise.

Algorithm for binding modes

The default binding mode (def_bm)

  1. The initial def_bm is provided by the outer checking context (or BindByValue if it is the outer-most pattern).

  2. non_ref_pat is defined as all pattern forms except:

  3. The pattern is a reference pattern, set def_bm = BindByValue.

  4. The pattern is a non_ref_pat and the scrutinee expression (e.g. match scrutinee { ... }) is of a reference type:

The binding mode

  1. When the pattern is an identifier pattern:

    • On $ident (@ $pat)?, the binding mode is def_bm.
    • On mut $ident (@ $pat)?, the binding mode is BindByValue.
    • Otherwise (ref mut? $ident (@ $pat)?), the binding mode is BindByReference.

    Then proceed recursively, passing the local def_bm, determining the binding modes of the sub-pattern if there is one (the $pat in @ $pat).

  2. Otherwise proceed recursively, passing the local def_bm, determining the binding modes of any sub-patterns.

When the binding mode is by-move

The binding mode is bind-by-move when it is BindByValue and the type is not Copy:

fn foo() {
    #[derive(PartialEq, Clone, Copy)] // `x` is by-value-copy ==> OK today.
    #[derive(PartialEq, Clone)] // `x` is by-value-move ==> ERROR today.
    struct S;

    match S {
        x // `x` is by-value.
            if x == S => drop(x),
        _ => {}
    }
}
@rust-highfive

This comment was marked as resolved.

Copy link
Collaborator

rust-highfive commented Aug 20, 2019

The job mingw-check of your PR failed (raw log). Through arcane magic we have determined that the following fragments from the build log may contain information about the problem.

Click to expand the log.
2019-08-20T02:08:46.6864535Z ##[command]git remote add origin https://github.com/rust-lang/rust
2019-08-20T02:08:46.7496296Z ##[command]git config gc.auto 0
2019-08-20T02:08:46.7561978Z ##[command]git config --get-all http.https://github.com/rust-lang/rust.extraheader
2019-08-20T02:08:46.7612370Z ##[command]git config --get-all http.proxy
2019-08-20T02:08:46.7796034Z ##[command]git -c http.extraheader="AUTHORIZATION: basic ***" fetch --force --tags --prune --progress --no-recurse-submodules --depth=2 origin +refs/heads/*:refs/remotes/origin/* +refs/pull/63118/merge:refs/remotes/pull/63118/merge
---
2019-08-20T02:09:21.4216525Z do so (now or later) by using -b with the checkout command again. Example:
2019-08-20T02:09:21.4216911Z 
2019-08-20T02:09:21.4217708Z   git checkout -b <new-branch-name>
2019-08-20T02:09:21.4218083Z 
2019-08-20T02:09:21.4219191Z HEAD is now at 1056d4a81 Merge 4d1197450880e428f0c23ce1a7fb48af1698c39e into c1b08dd26036e14f061b99b20cd6f169e29046f3
2019-08-20T02:09:21.4380266Z ##[section]Starting: Collect CPU-usage statistics in the background
2019-08-20T02:09:21.4382703Z ==============================================================================
2019-08-20T02:09:21.4382751Z Task         : Bash
2019-08-20T02:09:21.4382805Z Description  : Run a Bash script on macOS, Linux, or Windows
---
2019-08-20T02:14:27.1421151Z    Compiling cc v1.0.35
2019-08-20T02:14:27.1508401Z     Checking core v0.0.0 (/checkout/src/libcore)
2019-08-20T02:14:35.3274108Z    Compiling libc v0.2.60
2019-08-20T02:14:36.2932528Z    Compiling build_helper v0.1.0 (/checkout/src/build_helper)
2019-08-20T02:14:37.1563179Z error[E0008]: cannot bind by-move into a pattern guard
2019-08-20T02:14:37.1563529Z     --> src/libcore/iter/adapters/mod.rs:1243:18
2019-08-20T02:14:37.1563790Z      |
2019-08-20T02:14:37.1564043Z 1243 |             Some(v @ Some(_)) if n == 0 => v,
2019-08-20T02:14:37.1564333Z      |                  ^^^^^^^^^^^ moves value into pattern guard
2019-08-20T02:14:37.1564971Z      = help: add `#![feature(bind_by_move_pattern_guards)]` to the crate attributes to enable
2019-08-20T02:14:37.1565007Z 
2019-08-20T02:14:37.8766949Z    Compiling cmake v0.1.38
2019-08-20T02:14:40.9757867Z    Compiling compiler_builtins v0.1.18
2019-08-20T02:14:40.9757867Z    Compiling compiler_builtins v0.1.18
2019-08-20T02:14:41.7983939Z error: aborting due to previous error
2019-08-20T02:14:41.7984852Z 
2019-08-20T02:14:41.7992260Z For more information about this error, try `rustc --explain E0008`.
2019-08-20T02:14:41.9280407Z error: Could not compile `core`.
2019-08-20T02:14:41.9281562Z warning: build failed, waiting for other jobs to finish...
2019-08-20T02:14:42.7242388Z error: build failed
2019-08-20T02:14:42.7266007Z command did not execute successfully: "/checkout/obj/build/x86_64-unknown-linux-gnu/stage0/bin/cargo" "check" "--target" "x86_64-unknown-linux-gnu" "-Zbinary-dep-depinfo" "-j" "2" "--release" "--color" "always" "--features" "panic-unwind backtrace compiler-builtins-c" "--manifest-path" "/checkout/src/libstd/Cargo.toml" "--message-format" "json-render-diagnostics"
2019-08-20T02:14:42.7277210Z failed to run: /checkout/obj/build/bootstrap/debug/bootstrap check
2019-08-20T02:14:42.7277537Z Build completed unsuccessfully in 0:02:37
2019-08-20T02:14:42.7328377Z == clock drift check ==
2019-08-20T02:14:42.7345358Z   local time: Tue Aug 20 02:14:42 UTC 2019
2019-08-20T02:14:42.7345358Z   local time: Tue Aug 20 02:14:42 UTC 2019
2019-08-20T02:14:42.8839863Z   network time: Tue, 20 Aug 2019 02:14:42 GMT
2019-08-20T02:14:42.8844460Z == end clock drift check ==
2019-08-20T02:14:55.6734467Z ##[error]Bash exited with code '1'.
2019-08-20T02:14:55.6771321Z ##[section]Starting: Checkout
2019-08-20T02:14:55.6772970Z ==============================================================================
2019-08-20T02:14:55.6773044Z Task         : Get sources
2019-08-20T02:14:55.6773111Z Description  : Get sources from a repository. Supports Git, TfsVC, and SVN repositories.

I'm a bot! I can only do what humans tell me to, so if this was not helpful or you have suggestions for improvements, please ping or otherwise contact @TimNN. (Feature Requests)

@rfcbot

This comment has been minimized.

Copy link

rfcbot commented Aug 20, 2019

Team member @Centril has proposed to merge this. The next step is review by the rest of the tagged team members:

No concerns currently listed.

Once a majority of reviewers approve (and at most 2 approvals are outstanding), this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up!

See this document for info about what commands tagged team members can give me.

@joshtriplett

This comment has been minimized.

Copy link
Member

joshtriplett commented Aug 23, 2019

I'm trying to understand better. Under what circumstances will the compiler use a bind-by-move pattern, and under what circumstances will it not? I saw the example, but that just shows me when the compiler will do so, not when it won't. How does the compiler detect the need for a bind-by-move?

@Centril Centril force-pushed the Centril:stabilize-bind-by-move branch from 3b1e2dd to aaa9762 Sep 7, 2019
@rfcbot

This comment has been minimized.

Copy link

rfcbot commented Sep 8, 2019

The final comment period, with a disposition to merge, as per the review above, is now complete.

As the automated representative of the governance process, I would like to thank the author for their work and everyone else who contributed.

The RFC will be merged soon.

@matthewjasper

This comment has been minimized.

Copy link
Contributor

matthewjasper commented Sep 9, 2019

@bors r+

@bors

This comment has been minimized.

Copy link
Contributor

bors commented Sep 9, 2019

📌 Commit aaa9762 has been approved by matthewjasper

@bors

This comment has been minimized.

Copy link
Contributor

bors commented Sep 9, 2019

⌛️ Testing commit aaa9762 with merge 45859b7...

bors added a commit that referenced this pull request Sep 9, 2019
Stabilize `bind_by_move_pattern_guards` in Rust 1.39.0

Closes #15287.

After stabilizing `#![feature(bind_by_move_pattern_guards)]`, you can now use bind-by-move bindings in patterns and take references to those bindings in `if` guards of `match` expressions. For example, the following now becomes legal:

```rust
fn main() {
    let array: Box<[u8; 4]> = Box::new([1, 2, 3, 4]);

    match array {
        nums
//      ---- `nums` is bound by move.
            if nums.iter().sum::<u8>() == 10
//                 ^------ `.iter()` implicitly takes a reference to `nums`.
        => {
            drop(nums);
//          --------- Legal as `nums` was bound by move and so we have ownership.
        }
        _ => unreachable!(),
    }
}
```

r? @matthewjasper
@rust-lang rust-lang deleted a comment from rust-highfive Sep 9, 2019
@rust-lang rust-lang deleted a comment from bors Sep 9, 2019
@rust-lang rust-lang deleted a comment from bors Sep 9, 2019
@bors

This comment has been minimized.

Copy link
Contributor

bors commented Sep 9, 2019

☀️ Test successful - checks-azure
Approved by: matthewjasper
Pushing 45859b7 to master...

@bors bors added the merged-by-bors label Sep 9, 2019
@bors bors merged commit aaa9762 into rust-lang:master Sep 9, 2019
5 checks passed
5 checks passed
homu Test successful
Details
pr Build #20190907.38 succeeded
Details
pr (Linux mingw-check) Linux mingw-check succeeded
Details
pr (Linux x86_64-gnu-llvm-6.0) Linux x86_64-gnu-llvm-6.0 succeeded
Details
pr (LinuxTools) LinuxTools succeeded
Details
@Centril Centril deleted the Centril:stabilize-bind-by-move branch Sep 9, 2019
bors added a commit to rust-lang/rust-clippy that referenced this pull request Sep 10, 2019
Remove feature gate bind-by-move

This feature was stabilized in rust-lang/rust#63118
changelog: none
netbsd-srcmastr pushed a commit to NetBSD/pkgsrc that referenced this pull request Nov 11, 2019
Pkgsrc changes:
 * Remove patch which no longer applies (but what about RPATH?)
 * Adapt a few patches to changed files upstream.

Upstream changes:

Version 1.39.0 (2019-11-07)
===========================

Language
--------
- [You can now create `async` functions and blocks with `async fn`,
  `async move {}`, and `async {}` respectively, and you can now call
  `.await` on async expressions.][63209]
- [You can now use certain attributes on function, closure, and function
  pointer parameters.][64010] These attributes include `cfg`, `cfg_attr`,
  `allow`, `warn`, `deny`, `forbid` as well as inert helper attributes used
  by procedural macro attributes applied to items. e.g.
  ```rust
  fn len(
      #[cfg(windows)] slice: &[u16],
      #[cfg(not(windows))] slice: &[u8],
  ) -> usize {
      slice.len()
  }
  ```
- [You can now take shared references to bind-by-move patterns in the
  `if` guards of `match` arms.][63118] e.g.
  ```rust
  fn main() {
      let array: Box<[u8; 4]> = Box::new([1, 2, 3, 4]);

      match array {
          nums
  //      ---- `nums` is bound by move.
              if nums.iter().sum::<u8>() == 10
  //                 ^------ `.iter()` implicitly takes a reference to `nums`.
          => {
              drop(nums);
  //          ----------- Legal as `nums` was bound by move and so we have ownership.
          }
          _ => unreachable!(),
      }
  }
  ```

Compiler
--------
- [Added tier 3\* support for the `i686-unknown-uefi` target.][64334]
- [Added tier 3 support for the `sparc64-unknown-openbsd` target.][63595]
- [rustc will now trim code snippets in diagnostics to fit in your terminal.]
  [63402] **Note** Cargo currently doesn't use this feature. Refer to
  [cargo#7315][cargo/7315] to track this feature's progress.
- [You can now pass `--show-output` argument to test binaries to print the
  output of successful tests.][62600]

\* Refer to Rust's [platform support page][forge-platform-support] for more
information on Rust's tiered platform support.

Libraries
---------
- [`Vec::new` and `String::new` are now `const` functions.][64028]
- [`LinkedList::new` is now a `const` function.][63684]
- [`str::len`, `[T]::len` and `str::as_bytes` are now `const` functions.][63770]
- [The `abs`, `wrapping_abs`, and `overflowing_abs` numeric functions are
  now `const`.][63786]

Stabilized APIs
---------------
- [`Pin::into_inner`]
- [`Instant::checked_duration_since`]
- [`Instant::saturating_duration_since`]

Cargo
-----
- [You can now publish git dependencies if supplied with a `version`.]
  [cargo/7237]
- [The `--all` flag has been renamed to `--workspace`.][cargo/7241] Using
  `--all` is now deprecated.

Misc
----
- [You can now pass `-Clinker` to rustdoc to control the linker used
  for compiling doctests.][63834]

Compatibility Notes
-------------------
- [Code that was previously accepted by the old borrow checker, but rejected by
  the NLL borrow checker is now a hard error in Rust 2018.][63565] This was
  previously a warning, and will also become a hard error in the Rust 2015
  edition in the 1.40.0 release.
- [`rustdoc` now requires `rustc` to be installed and in the same directory to
  run tests.][63827] This should improve performance when running a large
  amount of doctests.
- [The `try!` macro will now issue a deprecation warning.][62672] It is
  recommended to use the `?` operator instead.
- [`asinh(-0.0)` now correctly returns `-0.0`.][63698] Previously this
  returned `0.0`.

[62600]: rust-lang/rust#62600
[62672]: rust-lang/rust#62672
[63118]: rust-lang/rust#63118
[63209]: rust-lang/rust#63209
[63402]: rust-lang/rust#63402
[63565]: rust-lang/rust#63565
[63595]: rust-lang/rust#63595
[63684]: rust-lang/rust#63684
[63698]: rust-lang/rust#63698
[63770]: rust-lang/rust#63770
[63786]: rust-lang/rust#63786
[63827]: rust-lang/rust#63827
[63834]: rust-lang/rust#63834
[63927]: rust-lang/rust#63927
[63933]: rust-lang/rust#63933
[63934]: rust-lang/rust#63934
[63938]: rust-lang/rust#63938
[63940]: rust-lang/rust#63940
[63941]: rust-lang/rust#63941
[63945]: rust-lang/rust#63945
[64010]: rust-lang/rust#64010
[64028]: rust-lang/rust#64028
[64334]: rust-lang/rust#64334
[cargo/7237]: rust-lang/cargo#7237
[cargo/7241]: rust-lang/cargo#7241
[cargo/7315]: rust-lang/cargo#7315
[`Pin::into_inner`]: https://doc.rust-lang.org/std/pin/struct.Pin.html#method.into_inner
[`Instant::checked_duration_since`]: https://doc.rust-lang.org/std/time/struct.Instant.html#method.checked_duration_since
[`Instant::saturating_duration_since`]: https://doc.rust-lang.org/std/time/struct.Instant.html#method.saturating_duration_since
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.