简体   繁体   English

为什么 Rust 不能在类型构造函数中强制可变引用到不可变引用?

[英]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 ,但如果类型不匹配发生在类型构造函数中,则它不起作用。

playground 操场

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):考虑检查一个空字符串,该字符串依赖于contentis_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(""))
}

(Playground) (操场)

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.

相关问题 当 Rust 中的 function 需要可变引用时,为什么不可变引用可以匹配? - Why an immutable reference can work in match when mutable reference is required by function in Rust? 为什么我不能通过可变值的可变引用的不可变引用来更新值? - Why can't I update a value through an immutable reference of a mutable reference of a mutable value? 为什么 Rust 允许对可变变量的不可变引用? - Why Does Rust Allow an Immutable Reference to a Mutable Variable? 如何使 Rust 可变引用不可变? - How to make a Rust mutable reference immutable? 为什么我可以使用可变变量使其生命周期与不可变引用重叠,但我不能以相同的方式使用可变引用? - Why can I use a mutable variable such that its lifetime overlaps with an immutable reference, but I can't use a mutable reference in the same way? 为什么 Rust 不能对不可变变量进行可变借用? - Why can't Rust do mutable borrow on an immutable varaiable? Rust 无法分配 Option 内部可变引用 - Rust can't assign Option internal mutable reference 在 Rust 中返回可变引用 - Returning a mutable reference in Rust 如何通过包含可变引用的引用使用不可变的Option? - How can you use an immutable Option by reference that contains a mutable reference? 为什么在 Rust 中将 const 引用直接转换为可变引用无效? - Why is casting a const reference directly to a mutable reference invalid in Rust?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM