< 返回版块

xcaptain 发表于 2019-04-04 23:20

想实现任意链表节点的拼接,用rust会出现运行时错误,而等价的C代码却能得到正确答案,这是为何

struct Node {
    elem: usize,
    next: Option<Box<Node>>,
}

fn main() {
    let mut n1 = Node {
        elem: 1,
        next: None,
    };
    let mut n2 = Node {
        elem: 2,
        next: None,
    };
    let n3 = Node {
        elem: 3,
        next: None,
    };

    n1.next = Some(Box::new(n2));
    n2.next = Some(Box::new(n3));

    let e = n1.next.unwrap().next.unwrap().elem;
    println!("{}", e);
}

编译运行后提示不能对None执行unwrap()

#include <stdio.h>
#include <stdlib.h>

typedef struct Node {
	int elem;
	struct Node *next;
} Node_t;

int main() {
	Node_t *n1 = malloc(sizeof(Node_t));
	n1->elem = 1;
	n1->next = NULL;

	Node_t *n2 = malloc(sizeof(Node_t));
	n2->elem = 2;
	n2->next = NULL;

	Node_t *n3 = malloc(sizeof(Node_t));
	n3->elem = 3;
	n3->next = NULL;

	n1->next = n2;
	n2->next = n3;

	printf("%d\n", n1->next->next->elem);
}

编译后运行能输出预期的3

为什么rust会出现这种情况,难道是Box::new(n2)的时候会复制一份n2以至于等后面更新n2之后原来那份保持不变?

评论区

写评论
lilydjwg 2019-04-06 18:28

完全不等价啊。Rust 的 node 分配在栈上,C 的在堆里。Rust 处理了内存的释放,C 的没有。

Aloxaf 2019-04-06 00:59

这个时候就该上 unsafe (误

嘛, 一般是用 Option<Rc<RefCell>> (

use std::rc::Rc;
use std::cell::RefCell;

struct Node {
    elem: usize,
    next: Option<Rc<RefCell<Node>>>,
}

fn main() {
    let mut n1 = Node {
        elem: 1,
        next: None,
    };
    let n2 = Node {
        elem: 2,
        next: None,
    };
    let n3 = Node {
        elem: 3,
        next: None,
    };

    let n2 = Rc::new(RefCell::new(n2));

    n1.next = Some(n2.clone());
    n2.borrow_mut().next = Some(Rc::new(RefCell::new(n3)));

    let e = n1.next.as_ref().unwrap().borrow().next.as_ref().unwrap().borrow().elem;
    println!("{}", e);
}
作者 xcaptain 2019-04-05 01:23

我用的是 rustc 1.33.0 (2aa4c46cf 2019-02-28) 直接编译没有warning,我觉得很奇怪本来应该报编译时错误的结果给我弄成运行时错误了。

因为编译器没有提示我使用了move的值,所以我一直在想为什么呢为什么呢。。

研究这个问题是因为我想知道有没有某种指针能实现既可以修改对应内存的值,又不占用所有权,现在我们知道n1.next=Box::new(n2)是会拿走n2的所有权的,有没有什么办法能使得n2移走之后还能继续添加元素,达到文中C代码的效果

Aloxaf 2019-04-05 00:37

第一反应: 这代码竟然能编译通过? 然后试了一下: 真能编译通过???

但是编译器是给了 warning 的, LZ 不要忽视啊!!

warning[E0382]: assign to part of moved value: `n2`
  --> src/main.rs:21:5
   |
11 |     let mut n2 = Node {
   |         ------ move occurs because `n2` has type `Node`, which does not implement the `Copy` trait
...
20 |     n1.next = Some(Box::new(n2));
   |                             -- value moved here
21 |     n2.next = Some(Box::new(n3));
   |     ^^^^^^^ value partially assigned here after move
   |
   = warning: this error has been downgraded to a warning for backwards compatibility with previous releases
   = warning: this represents potential undefined behavior in your code and this warning will become a hard error in the future

简要地讲, 在你执行 n1.next = Some(Box::new(n2)); 的时候, n2 就已经被 move 掉了, 你后面再对 n2.next 赋值已经是未定义行为了, 只不过为了向后兼容这个暂时没有被当做 error.

解决方法很简单, 把这两行赋值语句调换一下顺序即可.

1 共 4 条评论, 1 页