-
Notifications
You must be signed in to change notification settings - Fork 13k
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
"cannot move out of" error using ==
with Box<Trait>
, where no move is needed
#31740
Comments
Doing |
When comparing two different boxes, the equality operation does take ownership of the second one, even though that's plainly impossible from the signature of |
This does not only occur for the reflexive case. See https://is.gd/MWU7Kw
|
I'm not sure what the difference is. I based that (“This only occurs for the reflexive (x == x) case”) on that this compiles: trait Trait { }
impl<T> Trait for T { }
impl<'a> PartialEq for Trait + 'a {
fn eq(&self, other: &Self) -> bool { false }
}
fn main() {
let x = Box::new(1) as Box<Trait>;
let y = Box::new(1) as Box<Trait>;
let equal = x == y;
} |
Ah, with durka's explanation it is clear. |
@bluss but it moves |
@bluss try adding another identical |
Triage: no change |
Just got this recently. Had asked on Stack Overflow, vikrrrr and zuurr figured out what was going on at a lower level. https://stackoverflow.com/questions/56050864/cannot-move-out-of-an-rc Here's the generated PartialEq for my example:
|
I encountered this yesterday and was quite confused by it for a while. I wonder if this issue also occurs with the other comparison operators? I'd written a new issue report for it today because the issue searcher wasn't finding this issue #31740 for me until I entered a title for my issue, so I may as well add what I boiled down my case to: trait Mine {}
impl Mine for i32 {}
impl PartialEq for dyn Mine {
fn eq(&self, _other: &dyn Mine) -> bool {
true
}
}
fn main() {
let b1: Box<dyn Mine> = Box::new(1);
let b2: Box<dyn Mine> = Box::new(1);
if b1 == b2 {}
if b1 == b2 {}
}
The following alternative form that is supposed to be equivalent (IIUC) works: let b1r: &Box<dyn Mine> = &b1;
let b2r: &Box<dyn Mine> = &b2;
if <Box<dyn Mine> as PartialEq>::eq(b1r, b2r) {}
if <Box<dyn Mine> as PartialEq>::eq(b1r, b2r) {} |
#75231 has a new example with use std::cell::RefCell;
use std::rc::Rc;
pub trait Trait {}
impl PartialEq<dyn Trait> for dyn Trait {
fn eq(&self, _other: &Self) -> bool {
todo!();
}
}
pub fn eq(a: &Rc<RefCell<dyn Trait>>, b: &Rc<RefCell<dyn Trait>>) -> bool {
*a == *b
} |
I ran into this bug when trying to auto-derive pub trait Trait {}
/* This impl is not sufficient and causes "the cannot move out of `*__self_1_0` which is behind a shared reference" error. */
impl PartialEq for Box<dyn Trait> {
fn eq(&self, _other: &Self) -> bool {
todo!();
}
}
/* Code fails to compile without this impl. */
impl PartialEq<&Self> for Box<dyn Trait> {
fn eq(&self, _other: &&Self) -> bool {
todo!();
}
}
#[derive(PartialEq)]
struct Foo {
x: Box<dyn Trait>
} |
See this example (also try macro expansion). It's based on @bluss's one. It gives us the following:
|
Not only |
Here is a repro using a custom type (playground): #![feature(coerce_unsized, unsize)]
use core::{marker::Unsize, ops::CoerceUnsized};
struct Foo<T: ?Sized>(*const T);
impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<Foo<U>> for Foo<T> {}
impl<T: ?Sized + PartialEq> PartialEq for Foo<T> {
fn eq(&self, _: &Self) -> bool { loop {} }
}
trait Trait {}
impl PartialEq for dyn Trait {
fn eq(&self, _: &Self) -> bool { loop {} }
}
fn test(x: Foo<dyn Trait>, y: Foo<dyn Trait>) {
x == y;
drop(y);
} Note line 6, the implementation of The strange thing is that no unsized coercion is necessary here (albeit they must probably be used elsewhere in order to obtain instances of Interestingly, if the
So it looks like the comparison operators are attempting to coerce from one unsized type (with |
I ran into this bug the other day but failed to discover this thread while trying to figure it out, so I ended up asking about it on StackOverflow. Now that I know about the bug and have already created a repro and SO question, I figured that I should add it to the set of examples: Why does the equality operator for owned trait object move the right operand? |
It seems like I think any fix to improve the situation would at a minimum have to break code like this: use std::cell::Cell;
trait Trait {}
impl PartialEq for dyn Trait + '_ {
fn eq(&self, _: &dyn Trait) -> bool {
true
}
}
// not the same lifetimes, only one direction of outlives: 'b: 'a
fn f<'a, 'b: 'a>(x: Cell<&'a (dyn Trait + 'a)>, y: Cell<&'b (dyn Trait + 'b)>) {
x == y;
} because even if we added a fix to "avoid the coercion if the type wouldn't change", such a check would realistically not be able to consider lifetimes. Additionally, it would of course affect some drop orders, though realistically, those were super surprising anyways. (As it drops the right argument early, but not the left). Here's a case with slices which stops working with a fn f(x: Box<[i32]>, y: Box<[i32; 3]>) {
x == y; // works (but consumes `y` by value)
} (nice that it works, but also kind-of confusing, especially regarding the drop-order) Here's a case with reference to pointer coercion: fn f(x: *const u8, y: &mut u8) {
x == y;
// PartialEq::eq(&x, &y); doesn't work
} though it's less bad because this still doesn't consume IMHO, long-term it could be reasonable to fully replace |
The following code has a compilation error, but it should be a valid program (playground)
(Updated code in 2021 edition)
x == y
.Box<[T]>
don't behave like thisThe error is the same when using this equality impl instead:
The text was updated successfully, but these errors were encountered: