Lints around ToOwned: bad and good, run¶
How to learn ToOwned › Beside the steps · the errors, not the warnings: Every ToOwned error, and its fix
Level: 201 · a reference, by lint
One line: Twenty-two warnings from rustc and clippy that concern ToOwned, clone, Cow and their neighbours — each with a program that triggers it, what the tool prints, a version that is silent, and when the lint is right — plus the mistakes no lint catches at all.
Every transcript was recorded on rustc 1.98.0 and clippy 0.1.98 by running the file shown, and a sample of them re-run byte for byte for this page; the lines dropped from each are only the documentation link and the #[warn(...)] note. Both programs of every pair are compiled on every build by check_fences.py. What the build does not re-run is clippy, so a later clippy can reword a warning.
Turning them on¶
rustc's lints and clippy's warn by default groups need nothing: cargo clippy prints them. The opt-in ones take a flag or a Cargo.toml entry:
Find the lint¶
| Lint | From | On by default? | Fires on |
|---|---|---|---|
noop_method_call |
rustc | yes | .clone() on a &str |
suspicious_double_ref_op |
rustc | yes | .clone() on a &&String |
mismatched_lifetime_syntaxes |
rustc | yes | -> Cow<str> borrowing from an argument |
unused_must_use |
rustc | yes | s.to_owned(); as a statement |
suspicious_to_owned |
clippy · suspicious | yes | .to_owned() on a Cow |
unnecessary_to_owned |
clippy · perf | yes | an owned copy made only to be borrowed back |
cmp_owned |
clippy · perf | yes | an owned copy made only to compare |
owned_cow |
clippy · style | yes | Cow<'_, String> |
ptr_arg |
clippy · style | yes | &String, &Vec<T> and &PathBuf parameters |
iter_cloned_collect |
clippy · style | yes | .iter().cloned().collect() on a slice |
map_clone |
clippy · style | yes | .map(\|n\| n.clone()) |
unnecessary_owned_empty_strings |
clippy · style | yes | &String::new() where a &str will do |
clone_on_copy |
clippy · complexity | yes | .clone() on an i32 |
useless_format |
clippy · complexity | yes | format! with nothing to format |
implicit_clone |
clippy · pedantic | no | .to_owned() and .to_vec() on values already owned |
assigning_clones |
clippy · pedantic | no | a = b.clone() and a = s.to_owned() |
inefficient_to_string |
clippy · pedantic | no | .to_string() on a &&str |
cloned_instead_of_copied |
clippy · pedantic | no | .cloned() on Copy items |
manual_string_new |
clippy · pedantic | no | "".to_owned() |
redundant_clone |
clippy · nursery | no | a copy of a value that is never used again |
str_to_string |
clippy · restriction | no | "hello".to_string() |
clone_on_ref_ptr |
clippy · restriction | no | rc.clone() |
rustc, on by default¶
noop_method_call¶
rustc · warn by default · since 1.73.0 · fires on .clone() on a &str
warning: call to `.clone()` on a reference in this situation does nothing
--> bad.rs:3:30
|
3 | let copy: &str = greeting.clone();
| ^^^^^^^^ help: remove this redundant call
|
= note: the type `str` does not implement `Clone`, so calling `clone` on `&str` copies the reference, which does not do anything and can be removed
Silent:
When it is right. Always right in concrete code: the call copies a reference and nothing else. It watches .clone() only — .to_owned() on the same &str is a real conversion, and on a &T whose T is not Clone it copies the reference just as silently (step 6).
suspicious_double_ref_op¶
rustc · warn by default · fires on .clone() on a &&String
fn main() {
let names = [String::from("ada"), String::from("grace")];
let refs: Vec<&String> = names.iter().collect();
for r in &refs {
let name = r.clone();
println!("{name}");
}
}
warning: using `.clone()` on a double reference, which returns `&std::string::String` instead of cloning the inner type
--> bad.rs:5:21
|
5 | let name = r.clone();
| ^^^^^^^^
|
Silent:
fn main() {
let names = [String::from("ada"), String::from("grace")];
let refs: Vec<&String> = names.iter().collect();
for r in &refs {
let name: String = String::clone(r);
println!("{name}");
}
}
When it is right. Right when you meant the String; if you only wanted the reference, write *r. Its blind spot is below: r.to_owned() does the same thing and nothing warns.
mismatched_lifetime_syntaxes¶
rustc · warn by default · since 1.89.0 · fires on -> Cow<str> borrowing from an argument
use std::borrow::Cow;
fn exclaim(s: &str) -> Cow<str> {
if s.ends_with('!') {
Cow::Borrowed(s)
} else {
Cow::Owned(format!("{s}!"))
}
}
fn main() {
println!("{} {}", exclaim("hi"), exclaim("hey!"));
}
warning: hiding a lifetime that's elided elsewhere is confusing
--> bad.rs:3:15
|
3 | fn exclaim(s: &str) -> Cow<str> {
| ^^^^ ^^^^^^^^ the same lifetime is hidden here
| |
| the lifetime is elided here
|
= help: the same lifetime is referred to in inconsistent ways, making the signature confusing
help: use `'_` for type paths
|
3 | fn exclaim(s: &str) -> Cow<'_, str> {
| +++
Silent:
use std::borrow::Cow;
fn exclaim(s: &str) -> Cow<'_, str> {
if s.ends_with('!') {
Cow::Borrowed(s)
} else {
Cow::Owned(format!("{s}!"))
}
}
fn main() {
println!("{} {}", exclaim("hi"), exclaim("hey!"));
}
When it is right. Right: Cow<'_, str> shows at a glance that the result borrows from s. It is about how the signature reads — the code was already correct.
unused_must_use¶
rustc · warn by default · to_owned and clone are #[must_use] since 1.27.0 · fires on s.to_owned(); as a statement
warning: unused return value of `std::borrow::ToOwned::to_owned` that must be used
--> bad.rs:3:5
|
3 | s.to_owned();
| ^^^^^^^^^^^^
|
= note: cloning is often expensive and is not expected to have side effects
help: use `let _ = ...` to ignore the resulting value
|
3 | let _ = s.to_owned();
| +++++++
Silent:
When it is right. Always right: a copy thrown away is an allocation for nothing. .to_string();, .to_vec(); and cow.clone().into_owned(); as statements do not warn.
Clippy, on by default¶
suspicious_to_owned¶
clippy · suspicious · warn by default · since 1.65.0 · fires on .to_owned() on a Cow
use std::borrow::Cow;
fn main() {
let cow: Cow<'_, str> = Cow::Borrowed("hello");
let copy = cow.to_owned();
println!("{copy}");
}
warning: this `to_owned` call clones the `Cow<'_, str>` itself and does not cause its contents to become owned
--> bad.rs:5:16
|
5 | let copy = cow.to_owned();
| ^^^^^^^^^^^^^^
|
help: depending on intent, either make the `Cow` an `Owned` variant
|
5 | let copy = cow.into_owned();
| ++
help: or clone the `Cow` itself
|
5 - let copy = cow.to_owned();
5 + let copy = cow.clone();
|
Silent:
use std::borrow::Cow;
fn main() {
let cow: Cow<'_, str> = Cow::Borrowed("hello");
let owned: String = cow.into_owned();
println!("{owned}");
}
When it is right. Right nearly every time — into_owned() was meant (step 8). If a copy of the Cow itself was really wanted, say .clone(), as its second help: offers.
unnecessary_to_owned¶
clippy · perf · warn by default · since 1.59.0 · fires on an owned copy made only to be borrowed back
fn shout(s: &str) -> String {
s.to_uppercase()
}
fn main() {
let word: &str = "hello";
println!("{}", shout(&word.to_owned()));
}
warning: unnecessary use of `to_owned`
--> bad.rs:7:26
|
7 | println!("{}", shout(&word.to_owned()));
| ^^^^^^^^^^^^^^^^ help: use: `word`
|
warning: unnecessary use of `to_vec`
--> bad_iter.rs:3:14
|
3 | for b in bytes.to_vec() {
| ^^^^^^^^^^^^^^
|
help: remove any references to the binding
|
3 - for b in bytes.to_vec() {
3 + for b in bytes {
|
Silent:
fn shout(s: &str) -> String {
s.to_uppercase()
}
fn main() {
let word: &str = "hello";
println!("{}", shout(word));
}
When it is right. Right: the allocation exists only to be lent straight back. Its reach is narrower than its name — word.to_string().as_str() and &owned.clone() on a String pass unflagged — and its docs list a false positive (rust-clippy#8148) for into_iter on a copy whose original is mutated later.
cmp_owned¶
clippy · perf · warn by default · fires on an owned copy made only to compare
fn main() {
let input: &str = "yes";
let expected = String::from("yes");
if input.to_owned() == expected {
println!("match");
}
}
warning: this creates an owned instance just for comparison
--> bad.rs:4:8
|
4 | if input.to_owned() == expected {
| ^^^^^^^^^^^^^^^^ help: try: `input`
|
Silent:
fn main() {
let input: &str = "yes";
let expected = String::from("yes");
if input == expected {
println!("match");
}
}
When it is right. Right: &str == String already works, so the copy compares nothing a borrow could not.
owned_cow¶
clippy · style · warn by default · since 1.87.0 · fires on Cow<'_, String>
use std::borrow::Cow;
fn describe(name: Cow<'_, String>) -> usize {
name.len()
}
fn main() {
let owned = String::from("ferris");
println!("{}", describe(Cow::Borrowed(&owned)));
}
warning: needlessly owned Cow type
--> bad.rs:3:27
|
3 | fn describe(name: Cow<'_, String>) -> usize {
| ^^^^^^ help: use: `str`
|
Silent:
use std::borrow::Cow;
fn describe(name: Cow<'_, str>) -> usize {
name.len()
}
fn main() {
let owned = String::from("ferris");
println!(
"{} {}",
describe(Cow::Borrowed(&owned)),
describe(Cow::Borrowed("crab"))
);
}
When it is right. Right: a Cow of the owned type cannot borrow a literal, and Cow<'_, str> can. It skips public items by default (avoid-breaking-exported-api), and CString/CStr are not a drop-in swap.
ptr_arg¶
clippy · style · warn by default · fires on &String, &Vec<T> and &PathBuf parameters
use std::path::PathBuf;
fn report(name: &String, scores: &Vec<i32>, file: &PathBuf) -> String {
let total: i32 = scores.iter().sum();
format!("{}: {total} in {}", name.to_uppercase(), file.display())
}
fn main() {
let name = String::from("ada");
let scores = vec![90, 85];
println!("{}", report(&name, &scores, &PathBuf::from("a.txt")));
}
warning: writing `&String` instead of `&str` involves a new object where a slice will do
--> bad.rs:3:17
|
3 | fn report(name: &String, scores: &Vec<i32>, file: &PathBuf) -> String {
| ^^^^^^^
|
help: change this to
|
3 - fn report(name: &String, scores: &Vec<i32>, file: &PathBuf) -> String {
3 + fn report(name: &str, scores: &Vec<i32>, file: &PathBuf) -> String {
|
warning: writing `&Vec` instead of `&[_]` involves a new object where a slice will do
--> bad.rs:3:34
|
3 | fn report(name: &String, scores: &Vec<i32>, file: &PathBuf) -> String {
| ^^^^^^^^^
|
help: change this to
|
3 - fn report(name: &String, scores: &Vec<i32>, file: &PathBuf) -> String {
3 + fn report(name: &String, scores: &[i32], file: &PathBuf) -> String {
|
warning: writing `&PathBuf` instead of `&Path` involves a new object where a slice will do
--> bad.rs:3:51
|
3 | fn report(name: &String, scores: &Vec<i32>, file: &PathBuf) -> String {
| ^^^^^^^^
|
help: change this to
|
3 - fn report(name: &String, scores: &Vec<i32>, file: &PathBuf) -> String {
3 + fn report(name: &String, scores: &Vec<i32>, file: &Path) -> String {
|
Silent:
use std::path::{Path, PathBuf};
fn report(name: &str, scores: &[i32], file: &Path) -> String {
let total: i32 = scores.iter().sum();
format!("{}: {total} in {}", name.to_uppercase(), file.display())
}
fn main() {
let name = String::from("ada");
let scores = vec![90, 85];
println!("{}", report(&name, &scores, &PathBuf::from("a.txt")));
}
When it is right. Right: a borrowed-half parameter accepts every caller (step 3). Noise only where a signature must match an existing function-pointer type. It does not fire on a &String used only inside format!("{name}").
iter_cloned_collect¶
clippy · style · warn by default · fires on .iter().cloned().collect() on a slice
fn main() {
let slice: &[i32] = &[1, 2, 3];
let v: Vec<i32> = slice.iter().cloned().collect();
println!("{v:?}");
}
warning: called `.iter().cloned().collect()` on a slice to create a `Vec`
--> bad.rs:3:28
|
3 | let v: Vec<i32> = slice.iter().cloned().collect();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: calling `.to_vec()` is both faster and more readable
|
3 - let v: Vec<i32> = slice.iter().cloned().collect();
3 + let v: Vec<i32> = slice.to_vec();
|
Silent:
When it is right. Right: to_vec is the slice's own ToOwned, and says so.
map_clone¶
clippy · style · warn by default · fires on .map(|n| n.clone())
fn main() {
let names = [String::from("ada"), String::from("grace")];
let copies: Vec<String> = names.iter().map(|n| n.clone()).collect();
println!("{copies:?}");
}
warning: you are using an explicit closure for cloning elements
--> bad.rs:3:31
|
3 | let copies: Vec<String> = names.iter().map(|n| n.clone()).collect();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `cloned` method: `names.iter().cloned()`
|
Silent:
fn main() {
let names = [String::from("ada"), String::from("grace")];
let copies: Vec<String> = names.to_vec();
println!("{copies:?}");
}
When it is right. Right. Applying its suggestion literally, .iter().cloned().collect(), then trips iter_cloned_collect above — so the good version goes straight to to_vec(). .map(|n| n.to_owned()) is not caught.
unnecessary_owned_empty_strings¶
clippy · style · warn by default · since 1.62.0 · fires on &String::new() where a &str will do
fn main() {
let parts = ["a", "b", "c"];
let joined = parts.join(&String::new());
let cleaned = "a-b".replace('-', &"".to_owned());
println!("{joined} {cleaned}");
}
warning: usage of `&String::new()` for a function expecting a `&str` argument
--> bad.rs:3:29
|
3 | let joined = parts.join(&String::new());
| ^^^^^^^^^^^^^^ help: try: `""`
|
warning: unnecessary use of `to_owned`
--> bad.rs:4:38
|
4 | let cleaned = "a-b".replace('-', &"".to_owned());
| ^^^^^^^^^^^^^^ help: use: `""`
|
Silent:
fn main() {
let parts = ["a", "b", "c"];
let joined = parts.join("");
let cleaned = "a-b".replace('-', "");
println!("{joined} {cleaned}");
}
When it is right. Right, for readability rather than speed — String::new() does not allocate. The second warning is unnecessary_to_owned catching &"".to_owned().
clone_on_copy¶
clippy · complexity · warn by default · fires on .clone() on an i32
warning: using `clone` on type `i32` which implements the `Copy` trait
--> bad.rs:3:13
|
3 | let m = n.clone();
| ^^^^^^^^^ help: try removing the `clone` call: `n`
|
Silent:
When it is right. Always right. n.to_owned(), which does the same, is not flagged by anything — see below.
useless_format¶
clippy · complexity · warn by default · fires on format! with nothing to format
fn main() {
let greeting = format!("hello");
let name: &str = "ferris";
let copy = format!("{name}");
println!("{greeting} {copy}");
}
warning: useless use of `format!`
--> bad.rs:2:20
|
2 | let greeting = format!("hello");
| ^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"hello".to_string()`
|
warning: useless use of `format!`
--> bad.rs:4:16
|
4 | let copy = format!("{name}");
| ^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `name.to_string()`
|
Silent:
fn main() {
let greeting = "hello".to_owned();
let name: &str = "ferris";
let copy = name.to_owned();
println!("{greeting} {copy}");
}
When it is right. Right. Its suggestion is .to_string(), which str_to_string below flags if you turn that on — the good version uses to_owned() and satisfies both.
Clippy, opt-in¶
implicit_clone¶
clippy · pedantic · allow by default · since 1.52.0 · fires on .to_owned() and .to_vec() on values already owned
fn main() {
let name = String::from("ferris");
let copy = name.to_owned();
let nums = vec![1, 2, 3];
let more = nums.to_vec();
println!("{name} {copy} {nums:?} {more:?}");
}
warning: implicitly cloning a `String` by calling `to_owned` on its dereferenced type
--> bad.rs:3:16
|
3 | let copy = name.to_owned();
| ^^^^^^^^^^^^^^^ help: consider using: `name.clone()`
|
warning: implicitly cloning a `Vec` by calling `to_vec` on its dereferenced type
--> bad.rs:5:16
|
5 | let more = nums.to_vec();
| ^^^^^^^^^^^^^ help: consider using: `nums.clone()`
|
Silent:
fn main() {
let name = String::from("ferris");
let copy = name.clone();
let nums = vec![1, 2, 3];
let more = nums.clone();
println!("{name} {copy} {nums:?} {more:?}");
}
When it is right. Right when the value is already owned: clone() says what happens. It also catches to_string() on a String and to_owned() on an Rc. It is off by default because some code writes to_owned on purpose, so the line keeps working if the type later becomes a borrow.
assigning_clones¶
clippy · pedantic · allow by default · since 1.78.0 · fires on a = b.clone() and a = s.to_owned()
fn main() {
let mut label = String::from("draft");
println!("{label}");
let src = String::from("final");
label = src.clone();
println!("{label} {src}");
label = "done".to_owned();
println!("{label}");
}
warning: assigning the result of `Clone::clone()` may be inefficient
--> bad.rs:5:5
|
5 | label = src.clone();
| ^^^^^^^^^^^^^^^^^^^ help: use `clone_from()`: `label.clone_from(&src)`
|
warning: assigning the result of `ToOwned::to_owned()` may be inefficient
--> bad.rs:7:5
|
7 | label = "done".to_owned();
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `"done".clone_into(&mut label)`
|
Silent:
fn main() {
let mut label = String::from("draft");
println!("{label}");
let src = String::from("final");
label.clone_from(&src);
println!("{label} {src}");
"done".clone_into(&mut label);
println!("{label}");
}
When it is right. Right when the target holds a buffer worth reusing — a String refilled in a loop (step 9). Noise when there is nothing to reuse; the rewrite reads worse.
inefficient_to_string¶
clippy · pedantic · allow by default · since 1.40.0 · fires only below MSRV 1.82 · fires on .to_string() on a &&str
#[clippy::msrv = "1.81"]
fn main() {
let words = ["alpha", "beta"];
let owned: Vec<String> = words.iter().map(|w| w.to_string()).collect();
println!("{owned:?}");
}
warning: calling `to_string` on `&&str`
--> bad.rs:4:51
|
4 | let owned: Vec<String> = words.iter().map(|w| w.to_string()).collect();
| ^^^^^^^^^^^^^ help: try dereferencing the receiver: `(*w).to_string()`
|
= help: `&str` implements `ToString` through a slower blanket impl, but `str` has a fast specialization of `ToString`
Silent:
#[clippy::msrv = "1.81"]
fn main() {
let words = ["alpha", "beta"];
let owned: Vec<String> = words.iter().map(|&w| w.to_owned()).collect();
println!("{owned:?}");
}
When it is right. History now: &&str stopped taking the slow path in Rust 1.82, so at a current MSRV clippy stays silent even when asked. It still fires for a crate that declares an older MSRV, which is what the attribute in the bad version does. (The inner form #![clippy::msrv = "1.81"] is E0658 on stable.)
cloned_instead_of_copied¶
clippy · pedantic · allow by default · since 1.53.0 · fires on .cloned() on Copy items
warning: used `cloned` where `copied` could be used instead
--> bad.rs:3:27
|
3 | let max = nums.iter().cloned().max();
| ^^^^^^ help: try: `copied`
|
Silent:
When it is right. Right: copied promises a cheap copy. Noise if the item type may stop being Copy.
manual_string_new¶
clippy · pedantic · allow by default · since 1.65.0 · fires on "".to_owned()
warning: empty String is being created manually
--> bad.rs:2:19
|
2 | let mut buf = "".to_owned();
| ^^^^^^^^^^^^^ help: consider using: `String::new()`
|
Silent:
When it is right. Consistency only — neither form allocates.
redundant_clone¶
clippy · nursery · allow by default · since 1.32.0 · fires on a copy of a value that is never used again
fn consume(s: String) -> usize {
s.len()
}
fn main() {
let name = String::from("ferris");
println!("{}", consume(name.clone()));
}
warning: redundant clone
--> bad.rs:7:32
|
7 | println!("{}", consume(name.clone()));
| ^^^^^^^^ help: remove this
|
note: this value is dropped without further use
--> bad.rs:7:28
|
7 | println!("{}", consume(name.clone()));
| ^^^^
Silent:
fn consume(s: String) -> usize {
s.len()
}
fn main() {
let name = String::from("ferris");
println!("{}", consume(name));
}
When it is right. Right here: the original is dropped unused. name.to_owned() gets the same warning. It lives in nursery because its analysis misses cases and can misjudge complex borrows.
str_to_string¶
clippy · restriction · allow by default · fires on "hello".to_string()
warning: `to_string()` called on a `&str`
--> bad.rs:2:21
|
2 | let s: String = "hello".to_string();
| ^^^^^^^^^^^^^^^^^^^ help: try: `"hello".to_owned()`
|
Silent:
When it is right. A house style, not a bug: to_owned for make this owned, to_string for format this. Noise for a team that prefers to_string() everywhere — the two cost the same (the measurements).
clone_on_ref_ptr¶
clippy · restriction · allow by default · fires on rc.clone()
use std::rc::Rc;
fn main() {
let a = Rc::new(String::from("shared"));
let b = a.clone();
println!("{a} {b} {}", Rc::strong_count(&a));
}
warning: using `.clone()` on a ref-counted pointer
--> bad.rs:5:13
|
5 | let b = a.clone();
| ^^^^^^^^^ help: try: `std::rc::Rc::<std::string::String>::clone(&a)`
|
Silent:
use std::rc::Rc;
fn main() {
let a = Rc::new(String::from("shared"));
let b = Rc::clone(&a);
println!("{a} {b} {}", Rc::strong_count(&a));
}
When it is right. Right when readers should see that only a pointer is copied (step 8). a.to_owned() slips past it — only implicit_clone catches that one.
What no lint catches¶
Each of these is silent under cargo clippy's defaults and under the lint that catches its .clone() twin. With clippy's pedantic, nursery and restriction groups all on, the first and third are still invisible; the notes say what sees the other two. They matter because to_owned is not one of the methods rustc's no-op lints watch.
fn main() {
let names = [String::from("ada"), String::from("grace")];
let refs: Vec<&String> = names.iter().collect();
for r in &refs {
let name = r.to_owned();
println!("{name}");
}
}
.to_owned() on a &&String hands back a &String, exactly as .clone() does — but only .clone() gets suspicious_double_ref_op. Step 5.
use std::rc::Rc;
fn main() {
let a = Rc::new(String::from("shared"));
let b = a.to_owned();
println!("{a} {b} {}", Rc::strong_count(&a));
}
.to_owned() on an Rc copies the pointer, and clone_on_ref_ptr — which catches rc.clone() — does not see it. implicit_clone does, if you turn it on. Step 8.
.to_owned() on an i32 is clone_on_copy's case under another name, and passes. Step 6.
fn main() {
let names = [String::from("ada"), String::from("grace")];
let copies: Vec<String> = names.iter().map(|n| n.to_owned()).collect();
println!("{copies:?}");
}
.map(|n| n.to_owned()) is map_clone's case, and passes. Only pedantic's redundant_closure_for_method_calls mentions it, suggesting ToOwned::to_owned as a path. Where Clone will not do, §10.
And the one that matters most: .to_owned() on a &T whose T is not Clone returns the &T with no warning from anything, and the first sign is E0308 wherever the result meets a type annotation — error 4.
Removed¶
clippy::string_to_string no longer exists. Asking for it prints only this — the case it covered, to_string() on a String, is implicit_clone's now:
warning: lint `clippy::string_to_string` has been removed: `clippy::implicit_clone` covers those cases
|
See also¶
- Every
ToOwnederror, and its fix — what the compiler refuses, where this page is what it merely warns about - Strict clippy — turning whole lint groups on for a project, and what that costs
- Where
Clonewill not do — code that does not compile at all, beside code that does - Lints around borrowing forever — the same kind of page for lifetimes in structs, and a pattern no lint flags
Po polsku¶
Dwadzieścia dwa ostrzeżenia rustc i clippy związane z ToOwned, clone i Cow: dla każdego program, który je wywołuje, dokładny komunikat (rustc 1.98.0, clippy 0.1.98), wersja bez ostrzeżenia i kiedy lint ma rację. Część działa domyślnie (cargo clippy), część trzeba włączyć flagą -W clippy::nazwa albo w sekcji [lints.clippy] w Cargo.toml.
Najważniejsza jest sekcja o tym, czego żaden lint nie łapie: .to_owned() na &&String, na Rc, na i32 czy w .map(|n| n.to_owned()) przechodzi bez słowa, choć te same wywołania z .clone() dostają ostrzeżenie. A .to_owned() na &T, gdy T nie jest Clone, zwraca referencję i pierwszym sygnałem jest dopiero błąd E0308.
Szukaj po polsku: clippy unnecessary_to_owned · clippy implicit_clone · rust suspicious_to_owned Cow