错误:在自动解引用T时达到了递归限制。

3
我想知道这是正常现象还是一个bug:
struct A<T> (T);

impl<T> Add<A<T>, A<T>> for A<T> 
where T: Add<T, T> + Deref<T> + Copy {
    fn add(&self, &A(b): &A<T>) -> A<T> {
        let A(a) = *self;
        A(a.add(&b))
    }
}

产生了这个错误:

<anon>:7:11: 7:12 error: reached the recursion limit while auto-dereferencing T [E0055]
<anon>:7         A(a.add(&b))

在将a.add(&b)替换为a+b时,编译不会出错。

playpen

a+b不应该只是a.add(&b)的简写吗?

1个回答

5

简短版本: 在实现 Deref<T> 的过程中,T 没有意义。由于方法调用和操作符调用在解除左侧引用时的差异,导致了一些问题,因为 a + b 不完全等同于 a.add(&b)

详细版本:

+ 操作符和 Add.add 在涉及到取引用方面有所不同。

+ 操作符 本身通过引用获取两个操作数。对于各自类型为 AB 的操作数 a + b,需要为 A 实现 Add<B, C> 并生成类型为 C 的值。正如所述,ab 被引用;它们自己默默地产生这些引用,没有猜测。以下是它们的工作原理:

let a = 1i;
let b = a + a;    // this one works
let c = a + &a;   // mismatched types: expected `int`, found `&int` (expected int, found &-ptr)
let d = &a + a;   // binary operation `+` cannot be applied to type `&int`
let e = &a + &a;  // binary operation `+` cannot be applied to type `&int`

在这里没有进行任何引用,因此不良的T: Deref<T>要求不会导致任何问题。

Add.add将两个值都作为引用传递。作为常规函数调用,它具有在必要时自动取消引用和引用左侧的能力。虽然右侧作为方法参数被原样传递,但左侧尽可能地被取消引用,以找到所有可能被add指代的方法。通常情况下,这很好并且会做你想要的事情,但在这种情况下,它不会,因为T(其中类型a)实现了Deref<T>。 因此,取消引用会得到一个T。然后T实现Deref<T>,取消引用成为T。更重要的是,T实现Deref<T>并且取消引用成为T。它继续这样做,直到达到递归限制。实际上,T实现Deref<T>根本没有意义。

为了比较,这里展示了一些关于方法调用add的演示:

let a = 1i;
let b = a.add(a);      // mismatched types: expected `&int`, found `int` (expected &-ptr, found int)
let c = a.add(&a);     // this one works (a reference to the LHS is taken automatically)
let d = (&a).add(a);   // mismatched types: expected `&int`, found `int` (expected &-ptr, found int)
let e = (&a).add(&a);  // this one works (LHS is dereferenced and then rereferenced)

感谢您清晰的解释!我添加了deref trait,认为它只是意味着T可以被解引用(以更正我的代码中的其他错误),但这并不是它真正的含义。但是,关于a+ba.add(&b)之间的区别的解释非常值得。 - BigEpsilon

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接