I have the following code:
struct Helper<F1: Fn()> {
f: F1,
}
struct User<F2: Fn()> {
h: Helper<F2>,
}
fn new_user<F3: Fn()>() -> User<F3> {
User {
// error: expected type parameter, found closure
h: Helper { f: || {} },
}
}
fn main(){}
So User
needs a Helper<F1>
with the type of F1 being specified by User
, in this case by the closure in new_user
.
This code fails compile with the error expected type parameter, found closure
in new_user
. As far as I have understood (see for instance this link), this is because the bound of type parameter F3
on new_user
is specified by the caller (or signature maybe?), and so although the closure implements the Fn()
trait, it won't be able to restrict the type parameter F3 to match the type of the closure. Instead, it is expected that new_user
should work with any given F3
, which it clearly won't.
So my question is: how do I fix this? Is there any way of expressing that I want new_user
to return a User
with F2
set to the type of the closure?
I tried to use a type inference placeholder:
// error: the type placeholder `_` is not allowed within types on item signatures
fn new_user() -> User<_> {
User {
h: Helper { f: || {} },
}
}
I could use Box, but this requires modifying Helper
, which isn't ideal in my practical case:
struct Helper {
f: Box<dyn Fn()>,
}
struct User {
h: Helper,
}
fn new_user() -> User {
User {
h: Helper { f: Box::new(|| {}) },
}
}
fn main(){}
I'm also experimenting a bit with replacing Fn()
with a custom trait, which I can implement for User
specifically, but so far it's been clunky.
Any suggestions?
答案 0 :(得分:1)
tl;dr; You probably want this:
fn new_user() -> User<impl Fn()> {
User {
h: Helper { f: || {} },
}
}
More details: Let's look into this:
fn new_user<F3: Fn()>() -> User<F3> {
User {
// error: expected type parameter, found closure
h: Helper { f: || {} },
}
}
This states that fn_user
"accepts" anything satisfying Fn()
, but the caller must specify it when calling fn_user
. However, to successfully type-check, the supplied type would have to be the same as the one used within the body of new_user
, (which is the type of || {}
). However, closures have anonymous types, so the caller of new_user cannot know this type, since it cannot look into the implementation of new_user
too find out what to supply for F3
.
As said, what you want is that new_user
returns a User<SomeConcreteType>
where SomeConcreteType
satisfies F3
. The caller does not need to - in fact it should not even - specify SomeConcreteType
, because it is inferred within new_user
. You can do this as follows:
fn new_user() -> User<impl Fn()> {
User {
h: Helper { f: || {} },
}
}
This way, you specify that new_user
can be called without any arguments and it returns a User
with some concrete type satisfying Fn()
.