简体   繁体   English

有没有办法使不可变的引用可变?

[英]Is there a way to make an immutable reference mutable?

I want to solve a leetcode question in Rust ( Remove Nth Node From End of List ). 我想解决Rust中的leetcode问题( 从列表末尾删除第N个节点 )。 My solution uses two pointers to find the Node to remove: 我的解决方案使用两个指针来查找要删除的Node

#[derive(PartialEq, Eq, Debug)]
pub struct ListNode {
    pub val: i32,
    pub next: Option<Box<ListNode>>,
}

impl ListNode {
    #[inline]
    fn new(val: i32) -> Self {
        ListNode { next: None, val }
    }
}

// two-pointer sliding window
impl Solution {
    pub fn remove_nth_from_end(head: Option<Box<ListNode>>, n: i32) -> Option<Box<ListNode>> {
        let mut dummy_head = Some(Box::new(ListNode { val: 0, next: head }));
        let mut start = dummy_head.as_ref();
        let mut end = dummy_head.as_ref();
        for _ in 0..n {
            end = end.unwrap().next.as_ref();
        }
        while end.as_ref().unwrap().next.is_some() {
            end = end.unwrap().next.as_ref();
            start = start.unwrap().next.as_ref();
        }
        // TODO: fix the borrow problem
        // ERROR!
        // start.unwrap().next = start.unwrap().next.unwrap().next.take();
        dummy_head.unwrap().next
    }
}

I borrow two immutable references of the linked-list. 我借用了链表的两个不可变的引用。 After I find the target node to remove, I want to drop one and make the other mutable. 找到要删除的目标节点后,我要删除其中一个并使另一个可变。 Each of the following code examples leads to a compiler error: 下面的每个代码示例都会导致编译器错误:

// ERROR
drop(end); 
let next = start.as_mut().unwrap.next.take();

// ERROR
let mut node = *start.unwrap()

I don't know if this solution is possible to be written in Rust. 我不知道这种解决方案是否可以用Rust编写。 If I can make an immutable reference mutable, how do I do it? 如果我可以使不可变的引用变得可变,该怎么办? If not, is there anyway to implement the same logic while making the borrow checker happy? 如果不是,是否在使借阅检查器满意的同时实现相同的逻辑?

Is there a way to make an immutable reference mutable? 有没有办法使不可变的引用可变?

No. 没有。

You could write unsafe Rust code to force the types to line up, but the code would actually be unsafe and lead to undefined behavior . 您可以编写不安全的Rust代码来强制类型对齐,但​​是该代码实际上是不安全的,并导致未定义的行为 You do not want this. 您不想要这个。


For your specific problem, see: 对于您的特定问题,请参阅:

The correct answer is that you should not be doing this. 正确的答案是您应该这样做。 This is undefined behavior, and breaks many assumptions made by the compiler when compiling your program. 这是未定义的行为,打破了编译器在编译程序时所做的许多假设。

However, it is possible to do this. 但是, 可以这样做。 Other people have also mentioned why this is not a good idea, but they haven't actually shown what the code to do something like this would look like. 其他人也提到了为什么这不是一个好主意,但实际上他们并未显示执行此操作所需的代码。 Even though you should not do this , this is what it would look like: 即使您不应该这样做 ,它也会是这样:

unsafe fn very_bad_function<T>(reference: &T) -> &mut T {
    let const_ptr = reference as *const T;
    let mut_ptr = const_ptr as *mut T;
    &mut *mut_ptr
}

Essentially, you convert a constant pointer into a mutable one, and then make the mutable pointer into a reference. 本质上,您将常量指针转换为可变指针,然后将可变指针转换为引用。

Here's one example why this is very unsafe and unpredictable: 这是为什么这是非常不安全且不可预测的一个示例:

fn main() {
    static THIS_IS_IMMUTABLE: i32 = 0;
    unsafe {
        let mut bad_reference = very_bad_function(&THIS_IS_IMMUTABLE);
        *bad_reference = 5;
    }
}

If you run this... you get a segfault. 如果运行此命令,则会出现段错误。 What happened? 发生了什么? Essentially, you invalidated memory rules by trying to write to an area of memory that had been marked as immutable. 本质上,您通过尝试写入已标记为不可变的内存区域来使内存规则无效。 Essentially, when you use a function like this, you break the trust the compiler has made with you to not mess with constant memory. 本质上,当您使用类似这样的函数时,您将破坏编译器对您的信任,以免破坏常量内存。

Which is why you should never use this, especially in a public API , because if someone passes an innocent immutable reference to your function, and your function mutates it, and the reference is to an area of memory not meant to be written to, you'll get a segfault. 这就是为什么您永远不要使用它, 尤其是在公共API中 ,因为如果有人传递了对您的函数的无害的不变引用,并且您的函数对其进行了突变,并且该引用指向的是不打算写入的内存区域,会出现段错误。

In short: don't try to cheat the borrow checker. 简而言之:不要试图欺骗借阅检查器。 It's there for a reason. 在那里是有原因的。

EDIT: In addition to the reasons I just mentioned on why this is undefined behavior, another reason is breaking reference aliasing rules. 编辑:除了我刚才提到的为什么这是未定义行为的原因外,另一个原因是违反了引用别名规则。 That is, since you can have both a mutable and immutable reference to a variable at the same time with this, it causes loads of problems when you pass them in separately to the same function, which assumes the immutable and mutable references are unique. 也就是说,由于您可以同时拥有对变量的可变引用和不可变引用,因此,当您将变量分别传递给同一函数时,就会带来很多问题,其中假定不可变引用和可变引用是唯一的。 Read this page from the Rust docs for more information about this. 请从Rust文档中阅读此页面 ,以获取有关此信息的更多信息。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM