< 返回版块

javin 发表于 2018-07-23 22:40

#[derive(Debug)]
struct Node {
    elem1: Vec<i32>,
    elem2: Vec<i32>,
}

impl Node {
    fn new() -> Self {
        Node {
            elem1: Vec::new(),
            elem2: Vec::new(),
        }
    }
}

fn test1() {
    let node1 = Node::new();
    let elem1 = node1.elem1;
    let elem2 = node1.elem2;
//    node1.elem1 = vec![1,2,3];
    println!("{:?}", elem1);
    println!("{:?}", elem2);

}

fn test2() {
    let node2 = Box::new(Node::new());
    let elem1 = node2.elem1;
    let elem2 = node2.elem2;
    println!("{:?}", elem1);
    println!("{:?}", elem2);
}

fn main() {
    test1();
    test2();

}

上面的代码中函数test2是编译不过的,rust编译器报了如下的错误:

30 |     let elem1 = node2.elem1;
   |         ----- value moved here
31 |     let elem2 = node2.elem2;
   |         ^^^^^ value used here after move

函数test1中将结构体Node中的一个成员move后,仍然可以访问它的第二个成员,但是在函数test2中,通过Box创建的Node节点后却不能像test1那样操作。 请各位高手帮忙看下这其中的具体差别和原因,多谢!

评论区

写评论
Sagittarius A* 2018-08-09 21:53

开启NLL之后,你的代码就可以编译了。

作者 javin 2018-07-27 23:45

这个网址有一个解释: https://stackoverflow.com/questions/44081865/ownership-tracking-in-rust-difference-between-boxt-heap-and-t-stack#

大概意思是栈上struct中field的所有权可以由编译器来追踪,但是堆上的(即使用Box分配的)不行。

laizy 2018-07-25 19:54

这是因为Node的Drop是编译器生成的。在把elem2移走之后,编译器会标记原来Node里的elem2为uninitialized状态,Drop的时候就不会调用elem2的析构函数。 如果你为Node手动实现Drop你就会发现你没法把elem2移出来了。

impl Drop for Node {
    fn drop(&mut self) {
        println!("drop Node")
    }
}
error[E0509]: cannot move out of type `Node`, which implements the `Drop` trait
  --> src/main.rs:43:21
   |
43 |     let mut elem2 = node1.elem2;
   |                     ^^^^^^^^^^^
   |                     |
   |                     cannot move out of here
   |                     help: consider using a reference instead: `&node1.elem2`

error: aborting due to previous error

@javin 事实上rust似乎也没有将Box的内存作为一个整体释放,请看下面的测试代码: #[derive(Debug)] struct SmallNode { elem1: i32, }

impl SmallNode { fn new(val:i32) -> Self { SmallNode { elem1: val } } }

impl Drop for SmallNode { fn drop(&mut self) { println!("drop SmallNode in Node") } }

#[derive(Debug)] struct Node { elem1: SmallNode, elem2: Vec, }

impl Node { fn new() -> Self { Node { elem1: SmallNode::new(5), elem2: Vec::new(), } } }

fn test3() -> Vec{ let node1 = Box::new(Node::new()); let mut elem2 = node1.elem2; elem2.push(15); elem2

}

fn main() { let elem2 = test3(); println!("elem2 of Node is {:?}", elem2); }

该程序的输出是: drop SmallNode in Node elem2 of Node is [15]

Node有两个field。在test3中,将elem2从Node中move走后,elem1还是被释放了。

@Pslydhh 不太确定原因。 但我个人猜测是因为通过Box创建的Node内存是在堆中开辟的,并且释放内存时也是把这个Box作为一个整体释放,所以这里整个Box已经转移到elem1上面了,之后再使用node2就违反规则了吧?

Pslydhh 2018-07-25 07:33

嗯 好像是这样的哈哈,期待有更深入研究的人来解答下这个问题

@javin 事实上rust似乎也没有将Box的内存作为一个整体释放,请看下面的测试代码: #[derive(Debug)] struct SmallNode { elem1: i32, }

impl SmallNode { fn new(val:i32) -> Self { SmallNode { elem1: val } } }

impl Drop for SmallNode { fn drop(&mut self) { println!("drop SmallNode in Node") } }

#[derive(Debug)] struct Node { elem1: SmallNode, elem2: Vec, }

impl Node { fn new() -> Self { Node { elem1: SmallNode::new(5), elem2: Vec::new(), } } }

fn test3() -> Vec{ let node1 = Box::new(Node::new()); let mut elem2 = node1.elem2; elem2.push(15); elem2

}

fn main() { let elem2 = test3(); println!("elem2 of Node is {:?}", elem2); }

该程序的输出是: drop SmallNode in Node elem2 of Node is [15]

Node有两个field。在test3中,将elem2从Node中move走后,elem1还是被释放了。

@Pslydhh 不太确定原因。 但我个人猜测是因为通过Box创建的Node内存是在堆中开辟的,并且释放内存时也是把这个Box作为一个整体释放,所以这里整个Box已经转移到elem1上面了,之后再使用node2就违反规则了吧?

作者 javin 2018-07-24 23:52

事实上rust似乎也没有将Box的内存作为一个整体释放,请看下面的测试代码:

#[derive(Debug)]
struct SmallNode {
    elem1: i32,
}

impl SmallNode {
    fn new(val:i32) -> Self {
        SmallNode {
            elem1: val
        }
    }
}

impl Drop for SmallNode {
    fn drop(&mut self) {
        println!("drop SmallNode in Node")
    }
}

#[derive(Debug)]
struct Node {
    elem1: SmallNode,
    elem2: Vec<i32>,
}

impl Node {
    fn new() -> Self {
        Node {
            elem1: SmallNode::new(5),
            elem2: Vec::new(),
        }
    }
}

fn test3() -> Vec<i32>{
    let node1 = Box::new(Node::new());
    let mut elem2 = node1.elem2;
    elem2.push(15);
    elem2

}

fn main() {
    let elem2 = test3();
    println!("elem2 of Node is {:?}", elem2);
}

该程序的输出是:

drop SmallNode in Node
elem2 of Node is [15]

Node有两个field。在test3中,将elem2从Node中move走后,elem1还是被释放了。

@Pslydhh 不太确定原因。 但我个人猜测是因为通过Box创建的Node内存是在堆中开辟的,并且释放内存时也是把这个Box作为一个整体释放,所以这里整个Box已经转移到elem1上面了,之后再使用node2就违反规则了吧?

123hc 2018-07-24 16:52
#[derive(Debug)]
struct Node {
    elem1: Vec<i32>,
    elem2: Vec<i32>,
}

impl Node {
    fn new() -> Self {
        Node {
            elem1: Vec::new(),
            elem2: Vec::new(),
        }
    }
}

fn test1() {
    let  node1 = Node::new();
    let mut elem1 = node1.elem1;
    let mut elem2 = node1.elem2;
    elem1.push(83);
    elem2.push(23);
    println!("{:?}", elem1);
    println!("{:?}", elem2);

}

fn test2() {
    let node2 = Box::new(Node::new());
    let node =*node2;
    let mut elem1 = node.elem1;
    let mut elem2 = node.elem2;
    elem1.push(333);
    elem2.push(443);
    println!("{:?}", elem1);
    println!("{:?}", elem2);
}

fn main() {
    test1();
    test2();

}
Pslydhh 2018-07-24 08:21

不太确定原因。 但我个人猜测是因为通过Box创建的Node内存是在堆中开辟的,并且释放内存时也是把这个Box作为一个整体释放,所以这里整个Box已经转移到elem1上面了,之后再使用node2就违反规则了吧?

1 共 7 条评论, 1 页