RustRover's Advent Calendar

Unwrap a new feature each day 🎁

A special gift: an Advent Calendar of RustRover with the features you might not even know about, all designed to help you progress in Rust. We’ll post one feature per day for 12 days – stay tuned! 🎄🦀

Advent of Code 2025

JetBrains is proudly sponsoring Advent Of Code for the fifth year in a row! We’re calling all Rust developers to showcase your skills — take your time or race for a chance to win a USD 150 Amazon Gift Card! Read the blog post for more details 👉

Santa has something for you 🎁

Show expanded macro

RustRover gives you quick ways to view expanded macros:

  • Place the caret on a macro call, press Option+Enter, and select Expand macro.
  • Or click the gutter icon (the small hammer) next to supported macro calls and choose Expand macro.

Generate Rust Structs from JSON

Quickly turn JSON into Rust structs directly in RustRover:

  1. Copy a sample JSON payload to your clipboard.
  2. Paste it in a Rust file where you want to generate the models.
  3. The IDE infers the types and generates all necessary nested structs required to model the original JSON payload.

Seamless Module File Refactoring 🎁

RustRover fully supports moving between single-file modules (foo.rs) and directory-based modules (foo/mod.rs), ensuring your code layout remains clean and manageable.

Promote Module to Directory:

  1. Right-click the module file (foo.rs) in the Project view.
  2. Select Refactor → Promote Module to Directory. Outcome: A new folder (foo/) is created, foo.rs is moved to foo/mod.rs, and all use/mod declarations are updated automatically.

Downgrade Module to File

  1. Right-click the module directory (foo/) containing mod.rs (or lib.rs).
  2. Select Refactor → Downgrade Module to File. Outcome: mod.rs (or lib.rs) is moved to foo.rs next to the folder, items are moved/merged, and references are fixed.

Quick Access

You can access these refactoring actions quickly:

  • Intention Action: Place the caret inside the module, press Option + Enter (macOS) and choose "Promote module to directory" or "Downgrade module to file."
  • Refactor This: Use the dedicated Refactor This shortcut (Ctrl+T on macOS; Ctrl+Alt+Shift+T on Windows/Linux) and select the action from the menu.

Important Notes

  • Promoting: This works only for leaf modules (modules without nested submodules).
  • Scope: Only the file layout and module paths are adjusted; Cargo and external crate settings remain unchanged.

Debugger: Parallel Stacks View 🎁

In RustRover, you can see Parallel Stacks that show all active call stacks across threads (and async tasks) at once, grouped to make concurrency easier to reason about.

With it, you can:

  • Spot Hotspots: Big counts on a function indicate many threads are there (e.g., contended lock, wait).
  • Compare States: See producer/consumer threads at a glance, distinguishing which ones are blocked versus running.
  • Navigate Fast: Click a node to jump to that frame’s source; switching nodes switches the active thread/frame.

How to Use:

  1. Pause execution or hit a breakpoint.
  2. Open the Debug tool window and find the Parallel Stacks (or Threads/Stacks) tab.
  3. Hover over nodes to see full function paths; click to focus a thread and view locals/variables.
  4. Use filters to:
    - Hide library frames (std/tokio/etc.) to focus on your code.
    - Group by function or by async task.
  5. Resume/step to watch stacks change live.

Rust Specifics:

  • On macOS, LLDB powers the view. Async Rust may show executor frames (tokio/async-std). Enabling “Hide non-project frames” clarifies futures’ poll chains.
  • Build your project with debuginfo (the dev profile) for accurate symbols. Optimized builds can collapse or inline frames, making the graph less representative.

Seamless Module File Refactoring 🎁

When you type mod foo; in Rust, you are declaring a module that should live in a separate file or folder. RustRover can create this module structure for you instantly.

Create the File/Folder in RustRover:

  1. Trigger the Action: After typing mod foo;, place the caret on foo and press Option + Enter.
  2. Select the Intention: Choose the intention action: “Create module ‘foo’”.
  3. Choose the Structure: Select between:
    Create file foo.rs
    Create directory foo with mod.rs
  4. Alternative Access: You can also use the lightbulb intention icon that appears in the gutter.

Implement Trait 🎁

RustRover can generate trait implementations for you, streamlining the process:

  1. Right-click the method call that needs a trait.
  2. Choose the intention action "Implement trait".
  3. Type the desired trait name into the code that is generated automatically.

Debugger: Disassembly View 🎁

You can view disassembly while debugging in RustRover.

Enable and open Disassembly:

  1. Start a debug session (ensure a breakpoint is hit).
  2. Open the Frames or Debugger tool window.
  3. Click the “Show disassembly” button in the toolbar (it looks like a CPU/assembly icon), or use the gear icon → Show Disassembly.
  4. The debugger will automatically switch to disassembly if symbols aren’t available, or when you step into external code.

Best Practices for Clear Disassembly:

  • Ensure Debug Info: Build your project with a debug profile (the default dev profile) or use flags like RUSTFLAGS="-C debuginfo=2" if necessary.
  • Optimize Off for Clearer ASM: Use the dev profile or manually set opt-level = 0 in your build configuration. Higher optimization levels aggressively reorder and inline frames, which makes the generated assembly less representative of your source code.

Convert Tuple Enum Variant to Struct Variant 🎁

RustRover has a built-in refactoring for converting tuple enum variants to struct variants.

How to Convert (Tuple → Struct):

  1. Put the caret on the enum variant (or on its pattern/constructor usage).
  2. Press Option + Enter to open the intention actions.
  3. Choose “Convert to struct variant.”
  4. The IDE will:
    - Change Variant(T1, T2) to Variant { field0: T1, field1: T2 } (or prompt you for names).
    - Update constructors and pattern matches across the codebase.
    - If names are needed, you will get an inline rename for each field; accept defaults (field0, field1, …) or type your own names.

Reverse Direction (Struct → Tuple):

  • Place the caret on a struct variant → Press Option + Enter → Choose “Convert to tuple variant.”

Alternative Access:

  • Use Refactor This (Ctrl + T on macOS; Ctrl + Alt + Shift + T on Windows/Linux) and pick the conversion intention.
  • You can also invoke the action from a usage site (such as match arms, let destructuring, or construction) to trigger a preview of changes.

Notes:

  • The refactoring works best when the project indexes cleanly; run cargo check first for accurate usages.
  • If the variant is public, the refactor will update items across the workspace crates that are open in the IDE; external crates will not be changed.

Convert unwrap()to ?Operator 🎁

RustRover can automatically replace unwrap() with the ? operator and adjust the function signature if needed.

How to Use It:

  1. Place the caret on unwrap() (or try/expect, where supported).
  2. Press Option + Enter to open the intention actions.
  3. Choose “Convert unwrap() to ?”.

What the IDE Does:

  • Replaces foo.unwrap() with foo?.
  • If the enclosing function does not return a Result, it offers to:
    - Change the return type to Result<_, E> (inferred).
    - Add Ok(...) around the final value if required.
  • If there is context loss (e.g., unwrap_err), it may suggest mapping the error via .map_err(...) or anyhow::Result if that crate is in use.

Tips:

  • This feature works with nested calls; repeat the intention as needed.
  • If multiple unwrap calls are in a block, you can apply the intention per occurrence or use Fix all for file (where available).
  • For custom error types, use map_err to convert, for example: call.try_something().map_err(MyError::from)?.

Convert Between if let and match (and Vice Versa) 🎁

RustRover provides an intention action to switch between if let and match.

Convert if letmatch:

  • Put the caret on the if let keyword (or pattern).
  • Press Option+Enter.
  • Choose "Convert to match"
  • The IDE creates a match expression with the pattern arm and a fallback (_) arm, moving the else block into the fallback.

Convert matchif let:

  • Put the caret on the match keyword.
  • Press Option+Enter
  • Choose "Convert to if let"
  • Works when the match is a simple two-arm pattern with a catch-all (_) or None/Err arm.
    The main pattern becomes: if let PATTERN = EXPR { … } else { … }

Notes:

  • For Result/Option matches with more than two arms or guards, conversion may be unavailable.
  • After conversion, use Option+Enter on unused variables to replace them with _ bindings if needed.

IJ Platform General Features: Local History 🎁

In RustRover, you can use Local History to view and restore changes without using VCS.

Access it:

From the editor (current file):

  • Right-click in the editor → Local History → Show History.

From the Project view (folder/file):

  • Right-click the item → Local History → Show History.

Use it:

  • A timeline appears with labeled snapshots (Save, Refactor, Run, etc.).
  • Select a timestamp to see a diff.
  • Restore:
    - Revert a chunk via the diff gutter arrows.
    - Or click Revert to This Revision to roll the entire file back.
  • Put a label:
    Right-click → Local History → Put Label… to mark a point you can return to later.

Notes:

  • Local History is stored locally per IDE on your machine (not shared).
  • Retention is limited by time/size (configurable in Settings → Appearance & Behavior → System Settings → Local History).

Add a Turbofish via Intention / Quick Fix 🎁

In RustRover, you can add a turbofish using an intention/quick fix:

  • Place the caret on a generic call that needs type arguments (e.g., parse(), collect(), into()).
  • If the type can’t be inferred, an inspection will appear. Press Option+Enter.

How to apply:

  • Place the caret on the call/expression → Option+Enter“Add explicit type arguments” (also known as Add turbofish).
  • The IDE inserts ::<...> after the function or method name, for example:

"42".parse::()
iter.collect::<Vec<_>>()

Tips:

  • You can also trigger it on traits like From / Into:

value.into()
// Option+Enter → Add explicit type →
value.into::()

  • If multiple choices exist, select a type from the suggested list.
  • To add placeholders, choose a type and edit it, or use _ for elided generics:

foo::<_, usize>(...)

Want to discover more?

Check out the most useful and popular topics to get you started with RustRover.