为什么会变成堆栈溢出?

时间:2020-02-07 20:10:44

标签: rust stack-overflow

一个简单的测试用例,由于堆栈溢出而失败:

// trait to say FnMut has a clone_box method
pub trait ClonableFnMut<A>: FnMut(A) {
    fn clone_box(&self) -> Box<dyn ClonableFnMut<A> + Send + 'static>;
}

// overridden .clone() for Box<ClonableFnMut> that calls .clone_box on f
impl<A: 'static> Clone for Box<dyn ClonableFnMut<A> + Send + 'static> {
    fn clone(&self) -> Self {
        self.clone_box()
    }
}

// .clone_box() on FnMut clones itself and wraps itself in a new Box
impl<A, F: FnMut(A) + Clone + Send + 'static> ClonableFnMut<A> for F {
    fn clone_box(&self) -> Box<dyn ClonableFnMut<A> + Send + 'static> {
        Box::new(self.clone())
    }
}

fn main() {
    let mut f: Box<dyn ClonableFnMut<u8> + Send + 'static> = Box::new(|_x|{});

    println!("{:?}", f(3));
    println!("{:?}", f.clone()(4));
}

理论上:

  1. 致电.clone()上的Box<ClonableFnMut>
  2. 自定义实现在内部.clone_box()上调用FnMut
  3. 内部FnMut现在可以自己调用.clone(),因为它已标记为克隆。
  4. .clone_box()在新Box中返回此克隆的FnMut(自身)

但实际上:

  1. .clone()上手动呼叫Box<ClonableFnMut>
  2. 在内部.clone_box()上调用Box<FnMut>
  3. 呼叫self.clone()似乎是self = box
  4. 再次调用
  5. 自定义Box<FnMut> clone(),从步骤1开始。

第4步发生的真正原因是什么?

1 个答案:

答案 0 :(得分:3)

实际上发生的事情略有不同:

  1. 有人在clone上致电Box ...
  2. ...在上调用clone_box的{​​{1}} ...
  3. ...在上再次调用Box 上的clone,结束循环。

发生这种情况是因为您对Box的实现只调用了Clone,但是self.clone_box()是具有clone_box特性的一种方法。由于全面实施,ClonableFnMut尤其是在Box<dyn ClonableFnMut<A> + Send + 'static>上实现了此特征,Box本身满足了F: FnMut(A) + Clone + Send + 'static的要求。

为避免这种情况,您需要强制Clone实现为clone_box的内容而不是Box本身的内容调用Box方法。有两种明显不同的样式方式:

  1. Replace self.clone_box()self.deref().clone_box()并在某处添加所需的导入use std::ops::Deref;

  2. 或者,replace self.clone_box()(**self).clone_box(),不需要额外导入,但看起来有点神秘。请注意,克隆&self的参数基本上是self: &Self的语法糖,因此第一个*将其从&Box<F>取消引用到Box<F>,第二个一个人再次将其取消引用到F。调用clone_box会自动将其引用到&F,因为它也需要&self