Description
I found this question on SO where two more or less identical calls to Rc::ptr_eq
return different results, depending on where the Rc<RefCell<_>>
is cast to a trait object but also how the program is compiled. It behaves inconsistently when the function in question is not inlined or when incremental compilation is turned on. I've whittled it down to a smaller example (though it can probably be reduced further):
use std::{cell::RefCell, rc::Rc};
struct S;
trait T {}
impl T for S {}
struct C {
r: Rc<RefCell<dyn T>>,
}
impl C {
fn has_dyn_other(&self, other: &Rc<RefCell<dyn T>>) -> bool {
Rc::ptr_eq(&self.r, other)
}
fn has_other<A: T + 'static>(&self, other: &Rc<RefCell<A>>) -> bool {
Rc::ptr_eq(&self.r, &(Rc::clone(other) as Rc<RefCell<dyn T>>))
}
}
#[test]
fn weird_trait_obj() {
let s = Rc::new(RefCell::new(S));
let c = C {
r: Rc::clone(&s) as Rc<RefCell<dyn T>>,
};
// passes
assert!(Rc::ptr_eq(&(Rc::clone(&s) as Rc<RefCell<dyn T>>), &c.r));
// passes
assert!(Rc::ptr_eq(
&(Rc::clone(&s) as Rc<RefCell<dyn T>>),
&(Rc::clone(&s) as Rc<RefCell<dyn T>>)
));
// passes
assert!(c.has_dyn_other(&(Rc::clone(&s) as Rc<RefCell<dyn T>>)));
// passes with `incremental = false` or `#[inline(always)]`, fails otherwise
assert!(c.has_other(&s));
}
I expected to see this happen: I would have thought either all or none of the asserts would pass regardless of how it's compiled. I don't know how Rc<RefCell<T>> as Rc<RefCell<dyn Trait>>
actually works, so I'm not sure which one. I'm guessing it looks more like Rc<dyn RefCell<Trait>>
where the Rc
points at a trait object that in turn points at the RefCell
as its instance? I don't know how else it might work.
Instead, this happened: That last test is sensitive to at least two distinct factors: is incremental compilation on and is C::has_other
inlined or not. Adding #[inline(always)]
makes the assert pass regardless of incremental compilation.
Meta
rustc --version --verbose
:
rustc 1.65.0 (897e37553 2022-11-02)
binary: rustc
commit-hash: 897e37553bba8b42751c67658967889d11ecd120
commit-date: 2022-11-02
host: x86_64-unknown-linux-gnu
release: 1.65.0
LLVM version: 15.0.0
Backtrace
thread 'weird_trait_obj' panicked at 'assertion failed: c.has_other(&s)', src/lib.rs:37:5
stack backtrace:
0: rust_begin_unwind
at /rustc/897e37553bba8b42751c67658967889d11ecd120/library/std/src/panicking.rs:584:5
1: core::panicking::panic_fmt
at /rustc/897e37553bba8b42751c67658967889d11ecd120/library/core/src/panicking.rs:142:14
2: core::panicking::panic
at /rustc/897e37553bba8b42751c67658967889d11ecd120/library/core/src/panicking.rs:48:5
3: rcrefcelltest::weird_trait_obj
at ./src/lib.rs:37:5
4: rcrefcelltest::weird_trait_obj::{{closure}}
at ./src/lib.rs:21:1
5: core::ops::function::FnOnce::call_once
at /rustc/897e37553bba8b42751c67658967889d11ecd120/library/core/src/ops/function.rs:248:5
6: core::ops::function::FnOnce::call_once
at /rustc/897e37553bba8b42751c67658967889d11ecd120/library/core/src/ops/function.rs:248:5