-
Notifications
You must be signed in to change notification settings - Fork 12.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
confusing "unused type parameter" diagnostic #53589
Comments
Type bounds don't matter when checking if a type parameter is used to fix this you can use use std::marker::PhantomData;
struct Cacher<T, U: Copy>
where T: Fn(U) -> U
{
calculation: T,
value: Option<u32>,
__phantom: PhantomData<U> // the name "__phantom" doesn't matter
}
struct Cacher<T, U: Copy>
where T: Fn(U) -> U
{
fn new(calculation: T) -> Self {
Self { calculation, value: None, __phantom: PhantomData }
}
}
fn main() {
let c = Cacher::new(|x| x);
println!("Hello, world!");
} But since you are building a cacher, you probably want to change struct Cacher<T, U: Copy>
where T: Fn(U) -> U
{
calculation: T,
value: Option<U>,
} For more information about this error, you can look into issue 35146 Now, going a bit further, we could generalize this Cacher using a Map (HashMap or BTreeMap), so that it can keep track of multiple inputs, but this is left as an exercise for the reader. edit: moved urls into links |
Oh, duh. I should have just changed it to To me, the word "unused" implies "you can delete it and there will be no name resolution errors", which is not the case here. And I think this confused me and that confusion prevented me from seeing the obvious fix. It sounds like what is meant by the diagnostic is something like "no member transitively contains or points to an object of type U". Also, instead of immediately jumping to the somewhat esoteric Here is a strawman proposed diagnostic:
|
Generally unused type parameters are used with respect to lifetimes, for example, implementing a slice struct with raw pointers. There clearly should be a lifetime, but we can't store it in the struct. struct Slice<'a, T> {
ptr: *const T,
__phantom: Phantom<&'a T>
} Here the |
You can read more about variance and |
Thanks. I understand the need for PhantomData. The code I was writing was totally pedestrian, safe rust code though, so seeing PhantomData mentioned in the diagnostic was distracting and confusing. I think we should try to avoid mixing the more esoteric PhantomData situation with pedestrian mistakes like the one in this bug. |
A first approximation of good output would be:
Ideally, we would detect that
or suggest removing the bound
I don't see how we could suggest the correct fix without being misleading more times than not (even though I'm thinking of a couple of heuristics we could use):
|
Is there a more precise way to say "U is not used in struct members"? The core misleading thing I think is saying "unused" when there are uses of the name. |
@chisophugis This seems like the best way to improve the situation, as it points at the problem directly and then we could make suggestions on how to fix it like what @estebank suggested. |
Hello @KrishnaSannasi: I implement this Because change the member by Could you give me some suggestion? Thanks. // Cacher.rs
use std::marker::PhantomData;
use std::mem;
use std::thread;
use std::time::Duration;
struct Cacher<T, U, R>
where
T: Fn(U) -> R
{
calculation: T,
value: Option<R>,
__phantom: PhantomData<U>,
}
impl<T, U, R> Cacher<T, U, R>
where
T: Fn(U) -> R
{
fn new(calculation: T) -> Self {
Self {
calculation,
value: None,
__phantom: PhantomData,
}
}
fn value(&mut self, arg: U) -> &R {
let current = &mut self.value;
match current {
Some(v) => v,
None => {
let v = (self.calculation)(arg);
mem::replace(current, Some(v));
let current = current.as_ref();
current.unwrap()
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn it_works() {
let mut expensive_result = Cacher::new(|input: i32| {
println!("calculating slowly...");
thread::sleep(Duration::from_secs(2));
input * 2
});
println!(
"Today, do {} pushups!",
expensive_result.value(1)
);
println!(
"Next, do {} situps!",
expensive_result.value(1)
);
let correctOutput = &2;
assert_eq!(correctOutput, expensive_result.value(1));
}
} Test command cargo test --package minigrep --lib cache::tests::it_works -- --nocapture Output ~/dev_kit/src_code/minigrep >>>> cargo test --package minigrep --lib cache::tests::it_works -- --nocapture
Finished dev [unoptimized + debuginfo] target(s) in 0.00s
Running target/debug/deps/minigrep-81d8885cf42afcb9
running 1 test
calculating slowly...
Today, do 2 pushups!
Next, do 2 situps!
test cache::tests::it_works ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 6 filtered out |
You could just do Here is what I would do struct Cacher<T, R>
{
calculation: T,
value: Option<R>,
}
impl<T, R> Cacher<T, R> {
fn new(calculation: T) -> Self {
Self {
calculation,
value: None,
}
}
fn value<U>(&mut self, arg: U) -> &R
where T: FnMut(U) -> R
{
match self.value.as_ref() {
Some(v) => v,
None => {
let v = (self.calculation)(arg);
self.value = Some(v);
self.value.as_ref().unwrap()
}
}
}
} |
…ewjasper,Centril Reword E0392 slightly Make it clearer that a type or lifetime argument not being used can be fixed by referencing it in a struct's fields, not just using `PhathomData`. CC rust-lang#53589.
…ewjasper,Centril Reword E0392 slightly Make it clearer that a type or lifetime argument not being used can be fixed by referencing it in a struct's fields, not just using `PhathomData`. CC rust-lang#53589.
@KrishnaSannasi Just like in cpp fn value<U>(&mut self, arg: U) -> &R
where T: FnMut(U) -> R
{
let current: &mut Option<R> = &mut self.value;
match current {
Some(v) => v,
None => {
let v: R = (self.calculation)(arg);
*current=Some(v);
current.as_ref().unwrap()
}
}
} It works! BTW, Could mention it in offical tutorial? Thanks. But fn value<U>(&mut self, arg: U) -> &R
where T: FnMut(U) -> R
{
match self.value.as_ref() {
Some(v) => v,
None => {
let v = (self.calculation)(arg);
self.value = Some(v);
self.value.as_ref().unwrap()
}
}
} It has compile error error[E0506]: cannot assign to `self.value` because it is borrowed
--> src/cache/mod.rs:27:17
|
20 | fn value<U>(&mut self, arg: U) -> &R
| - let's call the lifetime of this reference `'1`
...
23 | match self.value.as_ref() {
| ---------- borrow of `self.value` occurs here
24 | Some(v) => v,
| - returning this value requires that `self.value` is borrowed for `'1`
...
27 | self.value = Some(v);
| ^^^^^^^^^^ assignment to borrowed `self.value` occurs here
error: aborting due to previous error
For more information about this error, try `rustc --explain E0506`.
error: Could not compile `minigrep`. |
pub fn value<U>(&mut self, arg: U) -> &R
where T: FnMut(U) -> R
{
if self.value.as_ref().is_none() {
self.value = Some((self.calculation)(arg));
}
self.value.as_ref().unwrap()
} Note that the This lifetime error likely won't happen by polonius when that lands, nll isn't great at handling return values. |
I really don't get this diagnostic. It says that U is unused, but removing U causes a " cannot find type
U
in this scope" diagnostic. Is U used or not? I feel like this diagnostic could be improved.(btw, I arrived at this code while reading https://doc.rust-lang.org/book/2018-edition/ch13-01-closures.html#limitations-of-the-cacher-implementation)
(Playground)
Errors:
The text was updated successfully, but these errors were encountered: