简体   繁体   English

引用 Rust 中 impl 块中的特征

[英]Referring to a trait within an impl block in Rust

I am working on translating a simple prolog implementation in Haskell to Rust for fun, and to get some more experience using the language.我正在努力将 Haskell 中的简单 prolog 实现翻译为 Rust 以获得乐趣,并获得更多使用该语言的经验。

In Haskell, I have a type class:在 Haskell 中,我有一个 class 类型:

class Unifiable t v | t -> v where
  variables :: t -> [v]
  subs      :: Unifier v t -> t -> t
  unify     :: t -> t -> (Maybe (Unifier v t))

Which I translated into the following trait in Rust:我在 Rust 中将其翻译成以下特征:

pub type Unifier<V,T> = HashMap<V,T>;

pub trait Unifiable<T,V> {
  fn variables(term: T) -> Vec<V>;
  fn subs(unifier: Unifier<V,T>, term: T) -> T;
  fn unify(firstTerm: T, secondTerm: T) -> Option<Unifier<V,T>>;

I then have the definition of a utility function that I would like to be able to use whenever an instance of Unifiable is available.然后,我定义了一个实用程序 function,我希望在Unifiable实例可用时能够使用它。 As a preliminary definition, I used this:作为初步定义,我使用了这个:

pub fn compose<V: Hash + Eq, T, U: Unifiable<T,V>>(first: Unifier<V,T>, other: Unifier<V,T>) -> Unifier<V,T> {
    let unifier: Unifier<V,T> = first.iter().map(|(&x,y)| (x, U::subs(other, *y))).collect();
    unifier.extend(other);
    return unifier;
}

which I am intending on being analogous to the Haskell type signature:我打算类似于 Haskell 类型签名:

compose :: Unifiable v t => Unifier v t -> Unifier v t -> Unifier v t

The problem is, I would like to use this helper function compose in an impl block for Unifiable , and I am not sure how to refer to to the Unifiable instance at the call site:问题是,我想在 Unifiable 的impl块中使用这个助手Unifiable compose ,我不确定如何在调用站点引用Unifiable实例:

impl <T: Eq, V: Clone + Eq + Hash> Unifiable<Term<T,V>,V> for Term<T,V> {
    ...
    fn unify(firstTerm: Term<T,V>, secondTerm: Term<T,V>) -> Option<Unifier<V,Term<T,V>>> {
        ....
        return Some(compose<V,Term<T,V>,X>(u, us));
        ....
    }
    ...
}

The issue being, I don't know what to use for X to refer to the Unifiable instance in the impl block I'm currently defining, and if I leave out the type parameters, I get a "cannot infer type parameter" error.问题是,我不知道X用什么来引用我当前定义的 impl 块中的 Unifiable 实例,如果我省略类型参数,我会收到“无法推断类型参数”错误。 Is this sort of reference with traits possible in Rust?在 Rust 中是否可以使用这种具有特征的参考?

Here are the differences between Haskell and Rust that are important to translating this code correctly:以下是 Haskell 和 Rust 之间的区别,这些区别对于正确翻译此代码很重要:

  • Haskell's type classes start with an undifferentiated collection of type parameters, but in Rust, a trait has one “special” parameter in addition to any generic parameters: the type the trait is implemented on. Haskell 的类型类从一个未区分的类型参数集合开始,但在 Rust 中,除了任何泛型参数之外,特征还有一个“特殊”参数:实现特征的类型。 In this case, it's natural for the term type T to be that type.在这种情况下,术语类型T是该类型是很自然的。
  • Relatedly, the Haskell "functional dependency" t -> v is expressed in Rust using an associated type instead of a type parameter, and this is equally important in Rust as in Haskell for aiding type inference. Relatedly, the Haskell "functional dependency" t -> v is expressed in Rust using an associated type instead of a type parameter, and this is equally important in Rust as in Haskell for aiding type inference.

Together, these mean that your trait can be written with no type parameters: T becomes Self , and V becomes declared as type V;总之,这些意味着你的 trait 可以在没有类型参数的情况下编写: T变成SelfV被声明为type V; and used as Self::V .并用作Self::V

pub trait Unifiable {
    type V;
    fn variables(term: Self) -> Vec<Self::V>;
    fn subs(unifier: Unifier<Self::V,Self>, term: Self) -> Self;
    fn unify(firstTerm: Self, secondTerm: Self) -> Option<Unifier<Self::V,Self>>;
}

Also, because one of the trait methods returns Self , we'll need the bound Self: Sized on the trait.此外,因为其中一个 trait 方法返回Self ,我们需要在 trait 上绑定Self: Sized

I've tinkered with your program until it compiles in the Rust Playground — hopefully this still matches your intent (I didn't check the details against my knowledge of unification algorithms).我已经修改了你的程序,直到它在 Rust Playground 中编译——希望这仍然符合你的意图(我没有根据我对统一算法的了解检查细节)。

Note: The T: Clone bound in compose arises because subs takes term: Self by value.注意: compose中的T: Clone绑定出现是因为subs采用term: Self值。 If implementations of subs will typically produce a new value without destroying the input, then the parameter type should be term: &Self instead and you can avoid needing T: Clone (and performing the clones) that way.如果subs的实现通常会在不破坏输入的情况下产生一个新值,那么参数类型应该是term: &Self而您可以避免需要T: Clone (并执行克隆)。 You will probably want to make another pass over your program and consider at each point whether parameters should be passed by-move or by-reference, but that is more informed by the implementation than the trait structure, so I can't give you detailed advice there.您可能希望对您的程序进行另一次传递,并在每一点考虑参数应该通过移动还是通过引用传递,但这比特征结构更受实现的影响,所以我不能给你详细信息那里的建议。

use std::hash::Hash;
use std::collections::HashMap;

pub type Unifier<V,T> = HashMap<V,T>;

pub trait Unifiable where Self: Sized {
  type V;
  fn variables(term: Self) -> Vec<Self::V>;
  fn subs(unifier: Unifier<Self::V,Self>, term: Self) -> Self;
  fn unify(firstTerm: Self, secondTerm: Self) -> Option<Unifier<Self::V,Self>>;
}

pub fn compose<V: Hash + Eq + Clone, T: Unifiable<V = V> + Clone>(
    first: Unifier<V, T>,
    other: Unifier<V, T>,
) -> Unifier<V, T> {
    let mut unifier: Unifier<V, T> = first
        .into_iter()
        .map(|(x, y)| (x, T::subs(other.clone(), y)))
        .collect();
    unifier.extend(other);
    unifier
}

#[derive(Clone, Debug, Eq, PartialEq)]
struct Term<V: Sized> {
    v: V,
}

impl <V: Clone + Eq + Hash> Unifiable for Term<V> {
    type V = V;
    fn variables(term: Self) -> Vec<V> {todo!();}
    fn subs(unifier: Unifier<V,Self>, term: Self) -> Self {todo!();}
    fn unify(firstTerm: Self, secondTerm: Self) -> Option<Unifier<V,Self>> {
        return Some(compose::<V,Self>(todo!(), todo!()));
    }
}

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=73f3957a502e33d46092945ca564b588 https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=73f3957a502e33d46092945ca564b588

(I have not edited this code for formatting and style, but note that it is idiomatic to use lower_case_with_underscores names instead of camelCase names.) (我没有编辑此代码的格式和样式,但请注意使用lower_case_with_underscores名称而不是camelCase名称是惯用的。)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM