Есть ли способ сделать неизменяемую ссылку изменяемой? - PullRequest
0 голосов
/ 17 января 2019

Я хочу решить вопрос с leetcode в Rust ( Удалить N-й узел из конца списка ). Мое решение использует два указателя, чтобы найти 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
    }
}

Я заимствую две неизменные ссылки в связанном списке. После того как я найду целевой узел для удаления, я хочу удалить один и сделать другой изменяемый. Каждый из следующих примеров кода приводит к ошибке компилятора:

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

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

Я не знаю, можно ли написать это решение на Rust. Если я могу сделать неизменную ссылку изменяемой, как мне это сделать? Если нет, то есть ли в любом случае возможность реализовать ту же логику, когда радует контролер заимствований?

Ответы [ 2 ]

0 голосов
/ 17 января 2019

Правильный ответ: вы не должны делать это. Это неопределенное поведение, и оно нарушает многие предположения, сделанные компилятором при компиляции вашей программы.

Однако, возможно сделать это. Другие люди также упоминали, почему это не очень хорошая идея, но на самом деле они не показали, как будет выглядеть код, чтобы сделать что-то подобное. Даже если вы не должны делать это , это выглядит так:

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
}

По сути, вы конвертируете постоянный указатель в изменяемый, а затем превращаете изменяемый указатель в ссылку.

Вот один пример, почему это очень небезопасно и непредсказуемо:

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

Если вы запустите это ... вы получите segfault. Что случилось? По сути, вы аннулировали правила памяти, пытаясь записать в область памяти, которая была помечена как неизменяемая. По сути, когда вы используете такую ​​функцию, вы разрушаете доверие компилятора к вам, чтобы не связываться с постоянной памятью.

Именно поэтому вы никогда не должны использовать это, , особенно в публичном API , потому что, если кто-то передает невинную неизменяемую ссылку на вашу функцию, и ваша функция изменяет ее, а ссылка относится к области память не предназначена для записи, вы получите ошибку.

Короче: не пытайтесь обмануть контролера заимствований. Это там по причине.

РЕДАКТИРОВАТЬ: В дополнение к причинам, которые я только что упомянул, почему это неопределенное поведение, другая причина нарушает правила наложения ссылок. То есть, поскольку вы можете одновременно иметь как изменяемую, так и неизменяемую ссылку на переменную, это вызывает массу проблем, когда вы передаете их отдельно в одну и ту же функцию, что предполагает, что неизменяемые и изменяемые ссылки уникальны. Прочитайте эту страницу из документации Rust для получения дополнительной информации об этом.

0 голосов
/ 17 января 2019

Есть ли способ сделать неизменяемую ссылку изменяемой?

номер

Вы можете написать небезопасный код Rust, чтобы заставить типы выстраиваться в линию, но код на самом деле был бы небезопасным и приводил к неопределенному поведению . Вы не хотите этого.


Для вашей конкретной проблемы см .:

...