[英]Why Rust can't coerce mutable reference to immutable reference in a type constructor?
It is possible to coerce &mut T
into &T
but it doesn't work if the type mismatch happens within a type constructor.可以将
&mut T
强制转换为&T
,但如果类型不匹配发生在类型构造函数中,则它不起作用。
use ndarray::*; // 0.13.0
fn print(a: &ArrayView1<i32>) {
println!("{:?}", a);
}
pub fn test() {
let mut x = array![1i32, 2, 3];
print(&x.view_mut());
}
For the above code I get following error:对于上面的代码,我收到以下错误:
|
9 | print(&x.view_mut());
| ^^^^^^^^^^^^^ types differ in mutability
|
= note: expected reference `&ndarray::ArrayBase<ndarray::ViewRepr<&i32>, ndarray::dimension::dim::Dim<[usize; 1]>>`
found reference `&ndarray::ArrayBase<ndarray::ViewRepr<&mut i32>, ndarray::dimension::dim::Dim<[usize; 1]>>`
It is safe to coerce &mut i32
to &i32
so why it is not applied in this situation?将
&mut i32
强制转换为&i32
是安全的,那么为什么在这种情况下不应用它呢? Could you provide some examples on how could it possibly backfire?你能否提供一些例子说明它怎么可能适得其反?
In general, it's not safe to coerce Type<&mut T>
into Type<&T>
.一般来说,将
Type<&mut T>
强制转换为Type<&T>
是不安全的。
For example, consider this wrapper type, which is implemented without any unsafe code and is therefore sound:例如,考虑这种包装器类型,它在没有任何不安全代码的情况下实现,因此是合理的:
#[derive(Copy, Clone)]
struct Wrapper<T>(T);
impl<T: Deref> Deref for Wrapper<T> {
type Target = T::Target;
fn deref(&self) -> &T::Target { &self.0 }
}
impl<T: DerefMut> DerefMut for Wrapper<T> {
fn deref_mut(&mut self) -> &mut T::Target { &mut self.0 }
}
This type has the property that &Wrapper<&T>
automatically dereferences to &T
, and &mut Wrapper<&mut T>
automatically dereferences to &mut T
.此类型具有
&Wrapper<&T>
自动取消引用&T
和&mut Wrapper<&mut T>
自动取消引用&mut T
的属性。 In addition, Wrapper<T>
is copyable if T
is.此外,如果
T
是,则Wrapper<T>
是可复制的。
Assume that there exists a function that can take a &Wrapper<&mut T>
and coerce it into a &Wrapper<&T>
:假设存在一个 function 可以采用
&Wrapper<&mut T>
并将其强制转换为&Wrapper<&T>
:
fn downgrade_wrapper_ref<'a, 'b, T: ?Sized>(w: &'a Wrapper<&'b mut T>) -> &'a Wrapper<&'b T> {
unsafe {
// the internals of this function is not important
}
}
By using this function, it is possible to get a mutable and immutable reference to the same value at the same time:通过使用这个 function,可以同时获得对相同值的可变和不可变引用:
fn main() {
let mut value: i32 = 0;
let mut x: Wrapper<&mut i32> = Wrapper(&mut value);
let x_ref: &Wrapper<&mut i32> = &x;
let y_ref: &Wrapper<&i32> = downgrade_wrapper_ref(x_ref);
let y: Wrapper<&i32> = *y_ref;
let a: &mut i32 = &mut *x;
let b: &i32 = &*y;
// these two lines will print the same addresses
// meaning the references point to the same value!
println!("a = {:p}", a as &mut i32); // "a = 0x7ffe56ca6ba4"
println!("b = {:p}", b as &i32); // "b = 0x7ffe56ca6ba4"
}
Full playground example 完整的游乐场示例
This is not allowed in Rust, leads to undefined behavior and means that the function downgrade_wrapper_ref
is unsound in this case.这在 Rust 中是不允许的,这会导致未定义的行为,并意味着 function
downgrade_wrapper_ref
在这种情况下是不合理的。 There may be other specific cases where you, as the programmer, can guarantee that this won't happen, but it still requires you to implement it specifically for those case, using unsafe
code, to ensure that you take the responsibility of making those guarantees.在其他特定情况下,作为程序员,您可以保证不会发生这种情况,但它仍然需要您专门针对这些情况使用
unsafe
的代码来实现它,以确保您有责任做出这些保证.
Consider this check for an empty string that relies on content
staying unchanged for the runtime of the is_empty
function (for illustration purposes only, don't use this in production code):考虑检查一个空字符串,该字符串依赖于
content
在is_empty
function 运行时保持不变(仅用于说明目的,不要在生产代码中使用它):
struct Container<T> {
content: T
}
impl<T> Container<T> {
fn new(content: T) -> Self
{
Self { content }
}
}
impl<'a> Container<&'a String> {
fn is_empty(&self, s: &str) -> bool
{
let str = format!("{}{}", self.content, s);
&str == s
}
}
fn main() {
let mut foo : String = "foo".to_owned();
let container : Container<&mut String> = Container::new(&mut foo);
std::thread::spawn(|| {
container.content.replace_range(1..2, "");
});
println!("an empty str is actually empty: {}", container.is_empty(""))
}
This code does not compile since &mut String
does not coerce into &String
.此代码无法编译,因为
&mut String
不会强制转换为&String
。 If it did, however, it would be possible that the newly created thread changed the content
after the format!
但是,如果确实如此,则新创建的线程可能会在
format!
后更改content
! call but before the equal comparison in the is_empty
function, thereby invalidating the assumption that the container's content was immutable, which is required for the empty check.调用但在
is_empty
function 中的相等比较之前调用,从而使容器内容不可变的假设无效,这是空检查所必需的。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.