What Rust version does this code actually need?
Paste your source. Get an instant estimate of the minimum Rust version required, each contributing feature listed, no rustup toolchain installs, no compile time.
How MSRVCheck works
- 1
Paste a Rust file or Cargo.toml
Drop in a .rs file, a snippet, or a whole Cargo.toml. Nothing is uploaded: parsing happens in your browser tab.
- 2
Comments and string literals are stripped
MSRVCheck sanitizes the source first, so a feature name mentioned in a comment or a test fixture string doesn't produce a false match.
- 3
Trait and impl blocks are extracted
For features that only matter inside a trait or impl (generic associated types, native async fn in traits, return-position impl Trait), MSRVCheck locates those blocks by brace-matching before checking inside them.
- 4
Each block and the rest of the source are pattern-matched
Every entry in the feature lookup table (let-else, OnceLock, is_some_and, and about 20 others) is checked against the sanitized source.
- 5
The newest matched feature sets the estimate
If your code uses a Rust 1.65 feature and a Rust 1.76 feature, the estimate is 1.76: code can only run on a toolchain new enough for its newest requirement.
- 6
Cargo.toml fields are cross-checked
If you pasted a manifest with a declared rust-version, MSRVCheck compares it against what it detected and flags it if your code has quietly outgrown your stated minimum.
What each detected category means
Every feature MSRVCheck flags falls into one of three categories. Knowing which one matters, since syntax features are unavoidable (the code won't parse at all on an older toolchain), while standard library features can sometimes be swapped for a crate that backports the same API.
A change to the language grammar itself, like let-else or the #[default] attribute on an enum variant. There is no workaround: code using this syntax will not parse on an older compiler, period.
let Ok(n) = input.trim().parse::<u32>() else {
return Err("not a number".into());
};A new type or method added to std, like OnceLock or Option::is_some_and. Often has a crate-based equivalent (once_cell, itertools) that works on older toolchains, so this category is the easiest to fix if you need to lower your MSRV.
use std::sync::OnceLock;
static CONFIG: OnceLock<Config> = OnceLock::new();A change to how traits work, like generic associated types, native async fn in traits, or return-position impl Trait in traits. Usually the hardest to replace: these change what the compiler can express, not just what's in std.
trait Repository {
type Item<'a> where Self: 'a;
async fn find(&self, id: u64) -> Option<Self::Item<'_>>;
}Full feature lookup table
Every feature MSRVCheck currently checks for, oldest to newest. This is the same table the tool matches your code against.
| Version | Feature |
|---|---|
| 1.60.0 | integer .abs_diff() |
| 1.62.0 | #[default] on an enum variant |
| 1.63.0 | array::from_fn |
| 1.65.0 | let-else statements |
| 1.65.0 | Generic associated types (GATs) |
| 1.70.0 | std::sync::OnceLock |
| 1.70.0 | std::cell::OnceCell |
| 1.70.0 | std::io::IsTerminal |
| 1.70.0 | Option::is_some_and |
| 1.70.0 | Result::is_ok_and |
| 1.73.0 | integer .div_ceil() |
| 1.74.0 | io::Error::other |
| 1.75.0 | Native async fn in traits |
| 1.75.0 | Return-position impl Trait in traits (RPITIT) |
| 1.76.0 | Result::inspect_err |
| 1.76.0 | ptr::from_ref / ptr::from_mut |
| 1.80.0 | std::sync::LazyLock |
| 1.80.0 | std::cell::LazyCell |
| 1.81.0 | #[expect(lint)] attribute |
| 1.82.0 | Option::is_none_or |
Recent Rust release dates
Rust ships a new stable release every six weeks. Useful for checking how old a pinned toolchain actually is.
| Version | Release date |
|---|---|
| 1.85.0 | |
| 1.84.0 | |
| 1.83.0 | |
| 1.82.0 | |
| 1.81.0 | |
| 1.80.0 | |
| 1.79.0 | |
| 1.78.0 | |
| 1.77.0 | |
| 1.76.0 | |
| 1.75.0 | |
| 1.74.0 | |
| 1.73.0 | |
| 1.72.0 | |
| 1.71.0 | |
| 1.70.0 | |
| 1.69.0 | |
| 1.68.0 | |
| 1.67.0 | |
| 1.66.0 | |
| 1.65.0 | |
| 1.64.0 | |
| 1.63.0 | |
| 1.62.0 | |
| 1.61.0 | |
| 1.60.0 |
When to use MSRVCheck
| Situation |
|---|
| Reviewing a contributor's PR |
| Evaluating a new dependency before adding it |
| Sanity-checking your own MSRV claim |
| Copying a snippet from a blog post or Stack Overflow |
| Learning when a specific syntax stabilized |
| Finalizing an MSRV before publishing a release |
Frequently Asked Questions
What does MSRVCheck do?
MSRVCheck scans pasted Rust source (or a Cargo.toml) against a lookup table of language and standard library features, and reports the newest one it finds. That's the estimated minimum Rust version the code needs, since using even one feature from Rust 1.75 means the code can't compile on a toolchain older than 1.75.
It runs entirely in your browser using regex pattern matching. There's no compilation, no toolchain installation, and no server involved.
How is MSRVCheck different from cargo-msrv?
| MSRVCheck | cargo-msrv | |
|---|---|---|
| Setup | None, paste and go | Install cargo-msrv + rustup toolchains |
| Method | Pattern match against a feature table | Actually compiles against a range of toolchains |
| Speed | Instant | Minutes, per toolchain tested |
| Accuracy | Heuristic estimate, lower bound only | Authoritative, verified by real compilation |
| Best for | Quick sanity check on a PR or dependency | Finalizing the MSRV for a release |
How accurate is the estimate?
It's a lower bound based on known syntax and API patterns, not a guarantee. If MSRVCheck finds a let-else statement, the code needs at least Rust 1.65, full stop, since that syntax didn't parse before then.
What it can't tell you is whether the code compiles cleanly on that version for other reasons: a dependency with its own higher MSRV, a compiler bug fix the code silently relies on, or a feature outside the lookup table entirely. Treat the result as a floor, not a certified MSRV.
Does MSRVCheck send my code anywhere?
No. All scanning happens in JavaScript inside your browser tab. Nothing you paste is sent to a server, logged, or stored. You can even disconnect from the internet after the page loads and MSRVCheck keeps working.
Can I check a Cargo.toml instead of source code?
Yes. Paste the whole Cargo.toml and MSRVCheck reads its edition and rust-version fields directly:
[package]
name = "my-crate"
edition = "2021"
rust-version = "1.70"edition = "2021" maps to a minimum of Rust 1.56, and edition = "2024" maps to Rust 1.85. If a rust-version field is present, MSRVCheck compares it against whatever it detects in your source and flags a mismatch if your code actually needs more than what you declared.
Why doesn't it flag async fn inside a trait using the async-trait crate?
Native async fn in a trait (stabilized in Rust 1.75) and the #[async_trait] macro from the async-trait crate look similar but have very different MSRVs. The macro rewrites your trait at compile time into something that works on any edition-2018+ toolchain.
MSRVCheck checks for an #[async_trait] attribute directly above the trait or impl block, and skips the async-fn-in-trait rule when it finds one, so a crate using the macro for broad compatibility doesn't get flagged with a Rust 1.75 requirement it doesn't actually have.
Can I check a whole crate or multiple files at once?
Not yet. MSRVCheck's MVP handles one pasted file at a time (source or manifest). For a full crate, run it over your highest-risk files individually, typically wherever a contributor's PR touched trait definitions or added a new dependency, or use cargo-msrv for a whole-crate authoritative check.