rust-lang/rust-analyzer · error
rename_to_self invoked on destructuring parameter
Error message
rename_to_self invoked on destructuring parameter
What it means
rename_to_self needs the first parameter to correspond to a single local binding so it can verify and convert it to self. When the first parameter is destructured (e.g. a tuple or struct pattern like `(a, b): (i32, i32)`), first_param.as_local returns None and the function bails, because a destructuring pattern cannot become a self parameter.
Source
Thrown at crates/ide/src/rename.rs:523
hir::ExpressionStoreOwner::Body(hir::DefWithBody::Function(func)) => func,
_ => bail!("Cannot rename local to self outside of function"),
};
if fn_def.self_param(sema.db).is_some() {
bail!("Method already has a self parameter");
}
let params = fn_def.assoc_fn_params(sema.db);
let first_param = params
.first()
.ok_or_else(|| format_err!("Cannot rename local to self unless it is a parameter"))?;
match first_param.as_local(sema.db) {
Some(plocal) => {
if plocal != local {
bail!("Only the first parameter may be renamed to self");
}
}
None => bail!("rename_to_self invoked on destructuring parameter"),
}
let assoc_item = fn_def
.as_assoc_item(sema.db)
.ok_or_else(|| format_err!("Cannot rename parameter to self for free function"))?;
let impl_ = match assoc_item.container(sema.db) {
hir::AssocItemContainer::Trait(_) => {
bail!("Cannot rename parameter to self for trait functions");
}
hir::AssocItemContainer::Impl(impl_) => impl_,
};
let first_param_ty = first_param.ty();
let impl_ty = impl_.self_ty(sema.db);
let (ty, self_param) = if impl_ty.is_reference() {
// if the impl is a ref to the type we can just match the `&T` with self directly
(first_param_ty.clone(), "self")
} else {
first_param_ty.as_reference_inner().map_or((first_param_ty.clone(), "self"), |ty| {View on GitHub (pinned to e8f7e90aa3)
Solutions
- Replace the destructured parameter with a single named parameter, then rename it to self.
- Destructure inside the function body instead (bind the parameter to a name, then `let (a, b) = param;`).
- Rename the binding to a non-self identifier.
Example fix
// before
fn f((a, b): (i32, i32)) {} // rename a -> self: error
// after
fn f(x: (i32, i32)) { let (a, b) = x; } // rename x -> self ok
Defensive patterns
Strategy: validation
Validate before calling
// Destructured first parameters cannot become self
// (e.g. `fn f((a, b): (i32, i32))`) - check the param is a simple binding
fn is_simple_binding(param_pattern: &str) -> bool {
param_pattern.chars().all(|c| c.is_alphanumeric() || c == '_' || c.is_whitespace())
&& !param_pattern.trim().is_empty()
} Try / catch
match rename(db, pos, "self", &config) {
Ok(change) => apply(change),
Err(e) if e.to_string().contains("rename_to_self invoked on destructuring parameter") => {
prompt_user("Destructured parameters cannot be renamed to self");
}
Err(e) => report(e),
} Prevention
- Name the first parameter with a simple identifier instead of a pattern
- Move destructuring into the function body via let bindings
- Skip rename-to-self refactors when the param list contains pattern parameters
When it happens
Trigger: Calling rename with new_name == "self" on a local that is a binding inside a destructured first-parameter pattern (tuple/struct/ref patterns), so the parameter has no single as_local.
Common situations: Users pressing F2 on a pattern-bound identifier inside a destructured parameter and typing `self`; refactoring tools over functions with pattern parameters.
Related errors
- Cannot rename local to self outside of function
- Method already has a self parameter
- Only the first parameter may be renamed to self
- No file available to rename
- Invalid name `{}`: cannot rename to a keyword
AI-assisted analysis of rust-lang/rust-analyzer@e8f7e90aa3 (2026-09-03).
Data as JSON: /api/errors/fd045eea262ffee4.
Report an issue: GitHub.