【问题标题】:Does type constructor implement Fn?类型构造函数是否实现了 Fn?
【发布时间】:2019-07-17 19:20:10
【问题描述】:

我不确定我的问题的标题是否正确,因为我不确定确切的位置。 假设我有一个看起来像这样的代码:


struct MyWrapper(u64);

fn my_func<F>(f: F, n: u64) -> MyWrapper
where
    F: Fn(u64) -> MyWrapper,
{
    f(n)
}

fn main() {
    my_func(MyWrapper, 3);
}

它编译和工作,所以它看起来像 MyWrapper 实现了 trait Fn

但是,我应该尝试在 trait 中使用它吗?

struct MyWrapper(u64);

trait MyTrait
where
    Self: Fn(u64) -> MyWrapper,
{
}

impl MyTrait for MyWrapper{}

我收到一个错误

16 | impl MyTrait for MyWrapper{};
   |      ^^^^^^^ expected an `Fn<(u64,)>` closure, found `MyWrapper`
   |
   = help: the trait `std::ops::Fn<(u64,)>` is not implemented for `MyWrapper`

这是一个更具理论性的问题。

说实话,我想要实现的是实现这样的特征

编辑: 我已经正确地指出,我的例子并不完整,所以有一个固定的版本。

pub enum Status {
    New,
    Cancelled,
}

struct NewTransaction(u64);

struct CancelledTransaction(u64);

fn get_by_status(id: &str, status: Status) -> Result<u64, ()> {
    Ok(3)
}

pub trait Transaction
where
    Self: std::marker::Sized,
{
    const status: Status;
    fn get(id: &str) -> Result<Self, ()>;
}

impl Transaction for NewTransaction {
    const status: Status = Status::New;
    fn get(id: &str) -> Result<Self, ()> {
        get_by_status(id, Self::status).map(Self)
    }
}

impl Transaction for CancelledTransaction {
    const status: Status = Status::Cancelled;
    fn get(id: &str) -> Result<Self, ()> {
        get_by_status(id, Self::status).map(Self)
    }
}

此代码可以编译,但正如您所见 - 每种类型的 Transaction 的所有实现都完全相同相同,因此将此实现作为默认实现移动似乎是完全合理的。像这样

pub trait Transaction
where
    Self: std::marker::Sized,
{
    const status: Status;
    fn get(id: &str) -> Result<Self, ()> {
        get_by_status(id, Self::status).map(Self)
    }
}

impl Transaction for NewTransaction {
    const status: Status = Status::New;
}

impl Transaction for CancelledTransaction {
    const status: Status = Status::Cancelled;
}

在这里,我抱怨说 Self 不能用作 Value。 我尝试通过在 trait 上引入条件 where Self: Fn(u32) -&gt; Self 来修复它,但它也不起作用。

编辑: 最后,我实现了 Sven Marnach 提出的想法——添加了一个方法 new 并要求所有结构都实现这个方法。它看起来仍然很奇怪,因为所有结构的实现都完全相同,但它确实有效。


pub trait Transaction
where
    Self: std::marker::Sized,
{
    const status: Status;
    fn new(n: u64) -> Self;
    fn get(id: &str) -> Result<Self, ()> {
        get_by_status(id, Self::status).map(Self::new)
    }
}

impl Transaction for NewTransaction {
    const status: Status = Status::New;
    fn new(n: u64) -> Self {
        Self(n)
    }
}

感谢大家的回答!

【问题讨论】:

  • “说实话,我想要实现的就是实现这样的trait”请完成你想要的真实案例,目前距离minimal reproducible example
  • impl MyTrait for MyWrapper{}; 在语法上不正确:删除分号,您仍然会收到您声称的错误消息。
  • trait MyTrait where Self: ... 通常应该写成trait MyTrait: ...
  • @SvenMarnach 很有趣。我同意,但是当编译器给出提示时,它会在此处建议 where 构造。

标签: rust


【解决方案1】:

当在期望值而不是类型的上下文中使用时,类似元组的结构或枚举变体的构造函数实际上被视为函数名,它被视为type 它在预期类型的​​上下文中命名。

当调用my_func(MyWrapper, 3) 时,名称MyWrapper 表示一个函数,其函数项类型强制转换为函数指针类型fn(u64) -&gt; MyWrapper。特别是,项目类型实现了 trait Fn(u64) -&gt; MyWrapper

然而,在代码impl MyTrait for MyWrapper {} 中,MyWrapper 表示它声明的结构类型。当在值上下文中使用时,该类型与 MyWrapper 的类型完全不同,并且它没有实现 Fn(u64) -&gt; MyWrapper 特征。

在您的实际用例中,我认为最简单的解决方案是要求 new() 方法在类型上具有所需的原型:

trait Payment {
   const status: Status;
   fn new(x: u64) -> Self;
   fn get(id: u64) -> Result<Self, Error> {
       get_by_status(Self::status, id).map(Self::new)
   }
}

Payment 的实现者只需向new() 方法提供所需的原型,但会继承get() 的默认实现。

【讨论】:

    【解决方案2】:

    它编译和工作,所以它看起来像 MyWrapper 实现了 trait Fn

    在 Rust 中了解某事物类型的一种快速而肮脏的方法是:

    struct MyWrapper(u64);
    
    fn main() {
        let mut foo = MyWrapper;
        foo = ();
    }
    

    这会产生错误:

    error[E0308]: mismatched types
     --> src/main.rs:5:11
      |
    5 |     foo = ();
      |           ^^ expected fn item, found ()
      |
      = note: expected type `fn(u64) -> MyWrapper {MyWrapper}`
                 found type `()`
    

    如您所见,foo 不是MyWrapper 结构,因此MyWrapper 并没有像您想象的那样实现Fn

    我同意这可能会造成混淆,请参阅 tuple struct case:

    带有括在括号中的字段的结构表达式构造一个元组结构。尽管这里列出的完整性是一个特定的表达式,但它等效于元组结构的构造函数的call expression。例如:

    struct Position(i32, i32, i32);
    Position(0, 0, 0);  // Typical way of creating a tuple struct.
    let c = Position;  // `c` is a function that takes 3 arguments.
    let pos = c(8, 6, 7);  // Creates a `Position` value.
    

    实际上,我想要实现的是实现这样的特征

    你的例子不完整,所以我的回答不是最终的,但我认为不可能做你想做的事。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-10-17
      • 2013-04-23
      • 1970-01-01
      • 2012-03-07
      • 2020-11-04
      • 2017-11-09
      • 1970-01-01
      • 2023-02-03
      相关资源
      最近更新 更多