-
Notifications
You must be signed in to change notification settings - Fork 13.2k
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
BorrowMut can cause confusion with Rc<RefCell<X>> #39232
Comments
I just encountered the same issue. |
@estebank @GuillaumeGomez Any ideas on what we could do to improve the situation here? It feels somewhat hard to detect... |
A bit yes... |
Ran into this one. |
I feel that we should just make it so that typeck resolves to the The diagnostic should still be improved regardless. @mitsuhiko could we get a minimized repro case? |
Minimized repro case: Works: use std::cell::RefCell;
use std::rc::Rc;
fn main() {
let rc = Rc::new(RefCell::new(true));
*rc.borrow_mut() = false;
} Fails: use std::borrow::BorrowMut;
use std::cell::RefCell;
use std::rc::Rc;
fn main() {
let rc = Rc::new(RefCell::new(true));
*rc.borrow_mut() = false;
} Error:
|
That'd be nice, but I fear that the impact might be larger than you are anticipating. I don't think that such a change is really on the table. Possibly the I'm trying to decide how a suggestion here ought to work: there are definitely a few cases where method calls resolve to surprising places and the resulting errors can be confused. I wonder if we can find some way to capture those too. For example, I am frequently hit by this problem, caused by a missing fn foo<T>(x: &T) {
let y = x.clone(); // results in `Clone` being invoked on the `&T`, rather than *failing*
} In that case -- as here -- method resolution succeeds, but the error comes much later. It feels like we would need some sort of "tracer" that we can apply to the type so that we can see that it was derived via a method dispatch, and then trace back to see if that method dispatch may have had other possibilities. I've been wanting to change how our type checker works -- it wouldn't necessarily have to be a massive change, basically just avoiding the |
I'm not a pro on the interior workings of rust, but it seems to me to be an issue of function-overloading. Why not use the PS: this would also help enormously with overloading in derived traits btw. |
@shiMusa we have an existing notation for that, you can write (for example) |
Thanks @nikomatsakis , I forgot about that. Yeah, it's not something you'd easily think of... |
I think the fact that a trait can shadow the methods of a type are particularly weird when working with trait objects. I came across this again in a different form when working with failure. rust-lang-deprecated/failure#211 failure implements a method on a trait with the same name as the default trait impl. I cannot find a way to disambiguate the call since I can't take the trait out of scope. use std::fmt::Debug;
trait Foo: Debug + 'static {
fn debug(&self) -> String where Self: Sized { format!("{:?}", self) }
}
impl Foo {
fn debug(&self) -> String { format!("{:?}", self) }
}
trait AsFoo {
fn as_foo(&self) -> &Foo;
}
impl<T: Bar> AsFoo for T {
fn as_foo(&self) -> &Foo { self }
}
trait Bar: Foo + AsFoo {}
impl Bar {
fn debug_via_bar(&self) { println!("debug: {}", self.as_foo().debug()) }
}
impl Foo for i32 {}
impl Bar for i32 {}
fn main() {
let x: &Bar = &42i32;
x.debug_via_bar();
} |
Encountered the same issue as OP. works: use std::collections::HashMap;
use std::cell::RefCell;
use std::rc::Rc;
let mut hm: HashMap<&str, i32> = HashMap::new();
hm.insert("myKey1", 100);
hm.insert("myKey2", 200);
let rc = Rc::new(RefCell::new(hm));
let v1 = rc.borrow_mut().get("myKey1").unwrap().clone();
println!("read {} into v1!", v1); error: use std::borrow::BorrowMut;
use std::collections::HashMap;
use std::cell::RefCell;
use std::rc::Rc;
let mut hm: HashMap<&str, i32> = HashMap::new();
hm.insert("myKey1", 100);
hm.insert("myKey2", 200);
let rc = Rc::new(RefCell::new(hm));
let v1 = rc.borrow_mut().get("myKey1").unwrap().clone();
println!("read {} into v1!", v1);
As a newcomer to Rust working through |
I've just been bitten by this. Given the proposed change in method resolution for arrays and
(Emphasis mine.) I'm reasonably certain 2021 edition is pretty much set in stone, but would this be in scope? |
I've also encountered this issue while learning Rust. |
One ugly workaround I have used for this scenario is to not "use std::borrow::BorrowMut" and write the fully qualified name when BorrowMut is needed. |
use
instead of
can be a work around of this problem since |
Looking at this again, I think we should teach the type error to detect this case in particular and suggest writing |
on second look, seem like rust-analyze automaticlly add |
I bit my teeth out on that problem for an hour. Any indication if this is going to be fixed? |
Just started with Rust and got hit by this, I didn't even realise I had |
Not necessarily a newcomer - I've probably been learning Rust for 6 months or so. (and >10 years of coding experience before this) I have given up on 3 projects where I encountered a need to use Rc<RefCell> and would visit countless websites to figure out how to work with X and thought I was doing the exact same thing as all the examples I saw. Stumbled upon this issue and sure enough BorrowMut was at the top. Don't know how it got there. I'm gonna complain to rust-analyzer about that. Deleted it and now everything in the world makes sense again. I'm just gonna go around and find all the guides I was looking at and share a link to this issue because that was flabbergasting. |
cc @rust-lang/wg-diagnostics I am wondering if, given the new developments in the Wearing my lang hat, I also wonder about the suggestion I made a few years ago (which I had totally forgotten about), that we might allow some kind of edition-relation annotation so that the |
#106150 provides the following suggestion:
|
Another suggestion that it could spit out is to not bring the conflicting trait into the scope at all. For instance instead of importing |
@mitsuhiko part of that can be added as an extra check (by looking to see if there are many methods with the same name in scope, instead of globally), but I would likely only change the wording of the suggestion at most, because I can't in this stage of the compiler verify whether the imported trait is used or not. The unused trait lint would complain by the time the code compiles, and then you're left with working but verbose code, which I feel isn't the worst outcome. |
I’m guessing that |
@mitsuhiko what do you think of the following? (Feel free to add comments to the PR so we don't spam everyone here.) |
…rrors Detect when method call on LHS might be shadowed Address rust-lang#39232.
Closing as per #106150. I don't think we'll provide a suggestion to remove the use, but we give enough context for someone to try that out, and once the call is changed to be fully qualified, the trait would be marked as unused. |
When
BorrowMut
is in scope and one has arc: Rc<RefCell<X>>
a call torc.borrow_mut()
does not deref into the innerRefCell
but uses the generic call returning a&mut Rc<...>
.Something like this:
The compiler should ideally give feedback that the trait was used instead of the deref and how to fix this issue.
The text was updated successfully, but these errors were encountered: