بِسْمِ ٱللَّهِ ٱلرَّحْمَـٰنِ ٱلرَّحِيمِ
Rust
How refac renames Rust files, moves whole modules with move-module, and renames Rust symbols with rust-analyzer.
The Rust backend keeps three jobs apart. A file rename inside its folder uses rust-analyzer. A module move to another place uses refac move-module, which works on the module tree through rust-analyzer's own semantic model. A symbol rename uses rust-analyzer too.
Required tooling
rust-analyzer is needed for symbol rename and for same-folder file renames. It is found through REFAC_RUST_ANALYZER, then PATH, then ~/.cargo/bin. rustup picks the toolchain from --project-path, so the rust-toolchain.toml of your project decides which rust-analyzer runs. If the component is missing, rustup's stand-in fails and refac reports the install as broken.
rustup component add rust-analyzer
refac doctor rust --project-path /path/to/cargo-workspacemove-module embeds the rust-analyzer libraries and needs no external binary.
Rename a module file
When source and target share a folder, the file name change is a module rename:
refac move \
--project-path /path/to/package \
--source-path src/domain/old_name.rs \
--target-path src/domain/new_name.rsrefac renames the module with rust-analyzer, which rewrites every use path that reached it across the crate, and then moves the file. Several renames in one call share one rust-analyzer session. --dry-run plans both the module rename and the file rename and counts the edits per file. A Cargo.lock that Cargo writes while the workspace loads is removed again if it did not exist before.
A target in another folder is refused, and the error says what to use instead (paths shortened here):
// Nothing was moved.
// Failed:
// Rust — Rust file move crosses a module boundary: src/engine/matching.rs -> src/domain/matching.rs. Use `refac move-module --project-path ... crate::<source> crate::<target>` so refac can move the complete logical module without introducing #[path] shims.Move a module
refac move-module --project-path /path/to/cargo-workspace \
crate::engine::matching \
crate::domain::matchingBoth arguments start with crate:: and name modules of the same crate. They are module paths and not file paths. --project-path may be a Cargo package or a workspace root. If the source path matches several workspace crates, refac stops and reports the matching declaration files.
The command moves the module together with its children: the file or the folder, mod.rs or the name.rs plus name/ pair. It changes the mod declarations, and it rewrites every use, crate::, super::, and self:: path that reached the module, in every crate of the workspace. No #[path] attribute and no re-export of the old path is added. Callers migrate to the new path.
What gets rewritten
crate::,super::, andself::paths. A leadingsuper::inside the moved file that now leaves the module becomes acrate::path.- Names imported with
usekeep their short form. Withuse super::matching;in place,matching::value()keeps its form and only the import is rewritten. A local variable or function that merely carries the module's name is not touched. - A module that leaves a grouped import (
use super::{helper, matching};) is taken out of the group and imported on its own line. - Paths in generic arguments (
Vec<old::Item>) and in code behind#[cfg(test)]. - Paths in macro arguments in every spelling:
vec![old::Item { .. }],assert_eq!(super::old::value(), 7), and$crate::paths. A different module of the same name is left alone. - The visibility of the
modline keeps its reach. A private module moved deeper is declaredpub(super)orpub(in crate::parent), so its old users still see it.
Preview and output
refac move-module --dry-run crate::engine::matching crate::domain::matching// Dry run: nothing was changed. Planned Rust module move:
crate::engine::matching -> crate::domain::matching
// move src/engine/matching.rs -> src/domain/matching.rs
// src/domain/mod.rs (2 edits)
// src/engine/mod.rs (1 edit)
// 3 edit(s) in 2 file(s); 1 path(s) would move.
// The Cargo check that proves the result still compiles was not made: add --check to make it on a copy of the workspace (as slow as a first build), or it runs on the real move.
// Run the same command without --dry-run to apply the move.--dry-run --check also compiles the result: the move is carried out on a throw-away copy of the workspace, without target, up to REFAC_DRY_RUN_COPY_MAX_MB (default 500), and cargo check --workspace --all-targets runs there. The copy builds into a folder of its own, so this takes as long as a first build, and a path dependency outside the workspace folder is not in the copy. Your project is not touched. A real move prints // Alhamdulillah Rust module moved semantically: followed by the old -> new line and the counts.
Safety
After the move, refac reloads the workspace to confirm that the new logical path resolves and the old one does not. It then runs cargo check --workspace --all-targets, which is the slow part. On refac's own repository it takes about 4 minutes. If the reload or the check fails, every changed file is put back, so a failed move inshallah won't leave your crate half moved.
Refused before anything is written
The layout must be the conventional one, and refac stops on a layout it cannot preserve with confidence. This is called strict v1:
- Inline source modules, and an inline descendant that declares an out-of-line child.
#[path]and#[macro_use]declarations, and any attribute on a module declaration other than a condition or a lint (cfg,allow,warn,deny,forbid,expect,doc,deprecated).pub(in ..)visibility that is not an absolutepub(in crate::..).- Rust syntax errors in a file that must be rewritten.
- Complex path syntax in a reference, for example a module path with generic arguments of its own, and an import with attributes that would have to leave its group.
- A target that already exists, or a target inside the source subtree.
- Resolved references outside the selected Cargo workspace. Cross-crate moves are not supported.
Proc-macro expansion and build-script output are disabled. The final Cargo check stays authoritative.
Rename a symbol
refac rename \
--project-path /path/to/cargo/root \
--file src/shapes.rs \
--symbol area \
--new-name surfacerefac renames a function, method, trait method, struct field, enum variant, type, constant, or local across the Cargo package or workspace, including tests/, examples/, and use trees. rust-analyzer follows the type system: a trait method rename changes every impl and every call, a re-exported struct is renamed through pub use, and a shorthand initializer such as Rect { width, height } renames the field and the local together.
- Macro bodies. rust-analyzer does not rename inside a
macro_rules!definition. When the old name is still written there, the report starts with anATTENTIONnote that names the file and line, and the build does not compile until you change it yourself. - Module names. Renaming a
modname is refused with a pointer tomove-module. - Leftovers. Strings, comments, and other symbols with the same name are listed in a note that ends with the
rg -wcommand.
Code behind cfg flags that are off for the server's configuration is not analysed, and the leftover note lists the lines that still spell the old name. After a rename, run cargo check. It should inshallah pass, and the ATTENTION note names the one place it cannot.