我怎样才能实现std :: convert :: From这样它不会消耗它的输入?

时间:2016-11-20 17:46:38

标签: rust traits borrow-checker

我设法让Rust类型检查器进入无限循环。一个非常类似的程序编译没有麻烦。为什么我不想编译程序?

为了节省您的时间和精力,我已经制作了两个程序的最小版本来隔离问题。当然,最小版本是一个毫无意义的程序。你必须用你的想象力去看我的动力。

成功

让我先介绍一下有效的版本。结构F<T>包裹TTarget类型可以从F<T>提供的T罐头转换。

struct F<T>(T);

impl<T> From<F<T>> for Target where Target: From<T> {
    fn from(a: F<T>) -> Target {
        let b = Target::from(a.0);
        f(&b)
    }
}

以下是一个示例来电者:

fn main() {
    let x = Target;
    let y = F(F(F(x)));
    let z = Target::from(y);
    println!("{:?}", z);
}

runs并打印"Target"

失败

函数f不使用其参数。如果From转换也没有使用它的参数,我会更喜欢它,因为类型F<T>可能很昂贵或无法克隆。我可以通过接受不可变借用而不是拥有值来编写与std::convert::From不同的自定义特征FromRef

trait FromRef<T> {
    fn from_ref(a: &T) -> Self;
}

当然,我最终想要使用From<&'a T>,但是通过定义我自己的特性,我可以更清楚地问我的问题,而不会弄乱生命周期参数。 (使用From<&'a T>)类型检查器的行为是相同的。

这是我的实施:

impl<T> FromRef<F<T>> for Target where Target: FromRef<T> {
    fn from_ref(a: &F<T>) -> Target {
        let b = Target::from_ref(&a.0);
        f(&b)
    }
}

这个编译。但是,main()功能并不是:

fn main() {
    let x = Target;
    let y = F(F(F(x)));
    let z = Target::from_ref(y);
    println!("{:?}", z);
}

gives a huge error message开头:

error[E0275]: overflow evaluating the requirement `_: std::marker::Sized`
  --> <anon>:26:13
   |
26 |     let z = Target::from_ref(y);
   |             ^^^^^^^^^^^^^^^^
   |
   = note: consider adding a `#![recursion_limit="128"]` attribute to your crate
   = note: required because of the requirements on the impl of `FromRef<F<_>>` for `Target`
   = note: required because of the requirements on the impl of `FromRef<F<F<_>>>` for `Target`
   = note: required because of the requirements on the impl of `FromRef<F<F<F<_>>>>` for `Target`
etc...

我做错了什么?

更新

randomly fixed it

问题是我忘了为FromRef<Target>实施Target

所以我现在想知道:编译器在想什么?我仍然无法将问题与错误消息联系起来。

1 个答案:

答案 0 :(得分:0)

您无法避免使用标准From / Into特征中的输入。

它们被定义为始终使用输入。它们的定义将输入和输出都指定为拥有的类型,并且具有不相关的生存期,因此您甚至不能通过尝试使用引用来“作弊”。

  • 如果要返回引用,则可以改为实现AsRef<T>。或者,如果您的类型是精简包装程序/智能指针,则为Deref<T>。您可以提供方法as_foo()

  • 如果要返回一个新的(拥有的)对象,则约定是提供to_foo()方法。