I am not sure that the title of my question is correct since I am not sure where exactly placed. Let say I have a code which looks like:
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);
}
It compiles and works so it looks like MyWrapper
implements trait Fn
.
However, should I try to use it in a trait.
struct MyWrapper(u64);
trait MyTrait
where
Self: Fn(u64) -> MyWrapper,
{
}
impl MyTrait for MyWrapper{}
I get an error
16 | impl MyTrait for MyWrapper{};
| ^^^^^^^ expected an `Fn<(u64,)>` closure, found `MyWrapper`
|
= help: the trait `std::ops::Fn<(u64,)>` is not implemented for `MyWrapper`
It was a more theoretical question.
Speaking practicaly, what I am trying to achieve is to implement trait like this
Edit: I have rightfully pointed out that my example is not full, so there is a fixed version.
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)
}
}
This code compiles, but as you can see - all implementations of Transaction for every type are exactly the same, so it seems totally reasonable to move this implementation as a default. Like this
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;
}
And here I got a complain that Self cannot be used as a Value.
I've tried to fix it by introducing a condition where Self: Fn(u32) -> Self
on trait but it didn't work either.
Edit:
In the end, I implemented the idea suggested by Sven Marnach - added a method new
and required all struct to implement this method. It still looks quite strange, since the implementation is exactly the same for all structures, but it works.
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)
}
}
Thanks everyone for answers!
The constructor of a tuple-like struct or enum variant is actually treated as a function name when used in a context where a value rather than a type is expected, and it is treated as the type it names in a context where a type is expected.
When calling my_func(MyWrapper, 3)
, the name MyWrapper
denotes a function with a function item type that coerces to the function pointer type fn(u64) -> MyWrapper
. In particular, the item type implements the trait Fn(u64) -> MyWrapper
.
In the code impl MyTrait for MyWrapper {}
, however, MyWrapper
denotes the struct type it declares. That type is completely different from the type of MyWrapper
when used in a value context, and it does not implement the Fn(u64) -> MyWrapper
trait.
In your actual use case, I believe the easiest solution is to require a new()
method with the desired prototype on the type:
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)
}
}
Implementors of Payment
will only need to provide new()
method with the desired prototype, but will inherit the default implementation of get()
.
It compiles and works so it looks like
MyWrapper
implements traitFn
.
A quick and dirty way to know the type of something in Rust is to do:
struct MyWrapper(u64);
fn main() {
let mut foo = MyWrapper;
foo = ();
}
This produces an error:
error[E0308]: mismatched types
--> src/main.rs:5:11
|
5 | foo = ();
| ^^ expected fn item, found ()
|
= note: expected type `fn(u64) -> MyWrapper {MyWrapper}`
found type `()`
As you can see foo
is not a MyWrapper
structure so MyWrapper
does not implement Fn
like you thought it would.
I agree this can be confusing, see tuple struct case
:
A struct expression with fields enclosed in parentheses constructs a tuple struct. Though it is listed here as a specific expression for completeness, it is equivalent to a call expression to the tuple struct's constructor. For example:
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.
Speaking practically, what I am trying to achieve is to implement trait like this
Your example is not complete so my answer is not final but I don't think it's possible to do what you want.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With