Rust 为什么生锈会';在类型构造函数中是否强制可变引用为不可变引用?

Rust 为什么生锈会';在类型构造函数中是否强制可变引用为不可变引用?,rust,immutability,coercion,type-constructor,Rust,Immutability,Coercion,Type Constructor,可以将&mut T T强制为&T,但如果类型构造函数中发生类型不匹配,则此操作无效 使用ndarray::*;//0.13.0 fn打印(a:&阵列视图1){ println!(“{:?}”,a); } pub-fn测试(){ 设mut x=array![1i32,2,3]; 打印(&x.view_mut()); } 对于上述代码,我得到以下错误: | 9 | print(&x.view_mut()); | ^^^^^^^^^^^^^ types

可以将
&mut T T
强制为
&T
,但如果类型构造函数中发生类型不匹配,则此操作无效

使用ndarray::*;//0.13.0
fn打印(a:&阵列视图1){
println!(“{:?}”,a);
}
pub-fn测试(){
设mut x=array![1i32,2,3];
打印(&x.view_mut());
}
对于上述代码,我得到以下错误:

  |
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]>>`
|
9 |打印(&x.视图_mut());
|^^^^^^^^^^^^^^^^^^^类型的可变性不同
|
=注意:应为引用“%ndarray::ArrayBase”`
找到引用“%ndarray::ArrayBase”`

强制
&mut i32
&i32
是安全的,那么为什么不在这种情况下应用它呢?您能否提供一些关于它如何可能适得其反的示例?

请考虑检查依赖于
内容的空字符串
is_empty
函数运行时保持不变(仅用于说明,请勿在生产代码中使用此选项):

struct容器{
内容:T
}
impl容器{
fn新(内容:T)->Self
{
自我{内容}
}
}
恳求{
fn为空(&self,s:&str)->bool
{
设str=format!(“{}{}”,self.content,s);
&str==s
}
}
fn main(){
让mut foo:String=“foo.”to_owned();
let container:container=container::new(&mut-foo);
std::thread::spawn(| |{
container.content.replace_范围(1..2,“”);
});
println!(“一个空str实际上是空的:{}”,container.is_empty(“”)
}


由于
&mut String
不会强制转换为
&String
,因此此代码不会编译。但是,如果是这样,那么新创建的线程可能会在
格式之后更改
内容
调用,但在
is _empty
函数中的相等比较之前,从而使容器内容不可变的假设无效,这是空检查所必需的。

一般来说,将
类型
强制为
类型
是不安全的

例如,考虑这个包装器类型,它在没有任何不安全代码的情况下实现,因此声音:

#[派生(复制、克隆)]
结构包装器(T);
包装器的impl-Deref{
类型Target=T::Target;
fn deref(&self)->&T::Target{&self.0}
}
包装器的impl-DerefMut{
fn deref_mut(&mut self)->&mut T T::Target{&mut self.0}
}
此类型具有以下属性:
&Wrapper
自动取消对
&T
的引用,
&mut-Wrapper
自动取消对
&mut-T
的引用。此外,
Wrapper
如果
T
是可复制的

假设存在一个函数,该函数可以接受一个
&包装器
,并将其强制为一个
&包装器


fn降级\u包装\u参考(w:&'a wrapper我想我已经知道了。如果我直接对字符串做同样的操作,我会得到
不能将foo作为不可变的借来,因为它也是作为可变的
借来的。另一方面,
容器
总是通过不可变的引用传递,而不管
内容
类型如何,所以这个机制不会被触发。不要如果没有安全锈迹中的包装器,我们是否也会遇到同样的情况?@pkubik我想这是一个不错的反例,我的代码应该更加明确,但是如果你在
println!
语句中添加类型注释,你的示例中会出现一个终生错误,而我的示例中不会。比较(更新、编译)与(已更新,不编译)。我将更新我的答案以包含此内容。(我猜问题在于它没有被用作可变引用,因此它实际上不会检查它是否确实是可变的。添加显式类型注释将确保可变引用以可变方式使用,这将导致终身错误,但不会。)是的,这实际上很有趣,编译器没有将引用变量的定义视为借用,您必须实际使用它来导致错误。我想它在组织代码时提供了更多的灵活性。
  |
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]>>`
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(""))
}