类型构造函数是否实现Fn?

时间:2019-07-17 19:20:10

标签: rust

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


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实现了特征Fn

但是,我应该尝试在特征中使用它吗。

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;
}

在这里,我抱怨自己不能用作价值。 我已经尝试通过在特征上引入条件where Self: Fn(u32) -> 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)
    }
}

感谢大家的回答!

2 个答案:

答案 0 :(得分:5)

类似元组的struct或enum变体的构造函数在需要值而不是类型的上下文中使用时,实际上被视为函数名称。 em> type 它在需要类型的上下文中命名。

调用my_func(MyWrapper, 3)时,名称MyWrapper表示一个函数,该函数的功能项类型强制为函数指针类型fn(u64) -> MyWrapper。特别是,项目类型实现特征Fn(u64) -> MyWrapper

但是,在代码impl MyTrait for MyWrapper{}中,MyWrapper表示您声明的结构类型。在值上下文中使用时,该类型与MyWrapper的类型完全不同,并且不实现Fn(u64) -> MyWrapper特质。

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

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

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

答案 1 :(得分:2)

  

它可以编译和运行,因此看起来像MyWrapper实现了特征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是{strong>不是一个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.

  

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

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