'Dealing with "expected fn pointer, found fn item"
I've been doing a lot of Google searching trying to figure this one out. I have a library that I would like for folks to be able to pass in a reference to a custom "signer" function (as an alternative to providing a secret key directly). This is the most recent problem I'm facing:
mismatched types
expected fn pointer, found fn item
note: expected enum `std::option::Option<&for<'r, 's> fn(&'r [u8; 20], &'s [u8; 32]) -> &[u8; 32]>`
found enum `std::option::Option<&for<'r, 's> fn(&'r [u8; 20], &'s [u8; 32]) -> &[u8; 32] {signer_func::<'_>}>`rustc(E0308)
check_handle.rs(28, 17): expected fn pointer, found fn item
I'm trying to pass that function in to a struct that gets passed through the system like this:
pub struct Params<'a> {
pub signer: Option<&'a fn(address: &[u8; 20], data: &[u8; 32]) -> &'a[u8; 32]>,
}
I've tried casting that function where I define it as I see suggested in most places, but the compiler complains that I can't cast non-primitive types:
pub fn signer_func<'a>(address: &[u8; 20], data: &[u8; 32]) -> &'a[u8; 32] {
. . .
}
. . .
let params = Params {
signer: Option::from(&signer_func as &fn(&[u8; 20], &[u8; 32]) -> &[u8; 32])
};
I've tried going down the road of using an Fn trait and redesigning my structs and functions around that, but keep getting stuck where I need to include the trait in the end function (I would get errors that the trait was undefined).
But is that the direction I need to go and focus on figuring out why that target function won't recognize the trait name? It feels like using the fn in the way I have it now is the most straightforward and the "pointer vs. item" seems like something that should be trivial to overcome, but I'm just not finding it.
EDIT
Okay, I've switched back to using the Fn trait and have this:
pub struct SignRequest {
pub address: [u8; 20],
pub data: [u8; 32]
}
pub struct Signer<CustomSigner>
where
CustomSigner: Fn(&SignRequest) -> [u8; 32],
{
pub address: [u8; 20],
pub data: [u8; 32],
pub sign: CustomSigner
}
impl<CustomSigner> Signer<CustomSigner>
where
CustomSigner: Fn(&SignRequest) -> [u8; 32],
{
pub fn new(address: [u8; 20], data: [u8; 32], sign: CustomSigner) -> Signer<CustomSigner> {
Signer { address, data, sign }
}
}
pub struct SignaturesParams<CustomSigner: for<'r> std::ops::Fn(&'r SignRequest) -> [u8; 32]> {
pub address: H160,
pub private_key: Option<H256>,
pub signer: Option<Signer<CustomSigner>>,
pub data: [u8; 32],
}
pub async fn signatures<CustomSigner: for<'r> std::ops::Fn(&'r SignRequest) -> [u8; 32]>(params: &SignaturesParams<CustomSigner>) -> Result<Signatures, Box<dyn std::error::Error + Sync + Send>> {
. . .
}
That compiles fine (the crazy looking "for" stuff was suggested and added by my IDE), but when I try to use the SignaturesParams in another module, I run into problems:
let signatures_params = SignaturesParams {
address: params.eth_address.clone(),
private_key: params.private_key.clone(),
data: hash_message(serde_json::to_string(&message)?),
signer: Option::None
};
type inside `async fn` body must be known in this context
cannot infer type for type parameter `CustomSigner` declared on the struct `SignaturesParams`rustc(E0698)
request.rs(34, 19): cannot infer type for type parameter `CustomSigner` declared on the struct `SignaturesParams`
I've tried various permutations of bringing that type into scope (e.g., including that long "for" line in the definition of the container function on that signatures_params block) but haven't located the right way to identify it yet.
I'm obviously fumbling a bit and the structure I have now is not optimal - I'm just trying to figure out how it all connects together. Thank you for your advice!
FINAL UPDATE
I ended up abandoning the flow I was trying to make work (i.e., defining a closure up front and passing that along through several structs to ultimately be executed by a terminating function). Instead, I broke the steps down in to smaller bits that allows me to pre-build the messages independently and then pass those messages and a custom closure directly to a Signer struct.
#[derive(Copy, Clone)]
pub struct SignData {
pub address: [u8; 20],
pub data: [u8; 32],
pub private_key: Option<[u8; 32]>,
}
#[derive(Clone)]
pub struct Signature {
pub data: String,
}
#[derive(Clone)]
pub struct Signer<F, Fut>
where
F: Fn(SignData) -> Fut,
Fut: Future<Output = Signature>,
{
pub sign_func: F,
}
impl<F, Fut> Signer<F, Fut>
where
F: Fn(SignData) -> Fut,
Fut: Future<Output = Signature>,
{
pub fn new(signer: F) -> Signer<F, Fut> {
Signer { sign_func: signer }
}
pub fn sign(self, sign_data: SignData) -> Fut {
(self.sign_func)(sign_data)
}
}
The closure to use with that can then be defined and used in this way.
let closure = async move |x: SignData| {
. . .
Signature { data: some_String }
};
let signature = Signer::new(closure).sign(some_SignData).await.data
I think that will work well for me at the moment. Thank you for your help!
Solution 1:[1]
The problem with your initial code is that while you can cast (or coerce) a fn item (what the compiler calls fn ... {name}) to fn pointer (fn ...), this property is not recursive: you cannot cast a reference to fn item to a reference to fn pointer. That is, signer_func as fn(&[u8; 20], &[u8; 32]) -> &'a [u8; 32] works, but &signer_func as &fn(&[u8; 20], &[u8; 32]) -> &'a [u8; 32] doesn't. The fix is simple: cast, then take the reference. That is, &(signer_func as fn(&[u8; 20], &[u8; 32]) -> &'a [u8; 32]).
&fn() doesn't look right at all, though: fn() is already a pointer, so &fn() is redundant double redirection. You may want to change that.
The problem in your second code is that the compiler cannot infer what is the type of CustomSigner, since you provided None. You need to specify this type parameter explicitly. I cannot tell what it needs to be without looking at the code, but assuming you don't really need it in this case, you can specify a dummy type, e.g. fn(&SignRequest) -> [u8; 32].
Solution 2:[2]
The problem is you're binding too many lifetimes together.
pub signer: Option<&'a fn(address: &[u8; 20], data: &[u8; 32]) -> &'a[u8; 32]>
This requires a reference (which exists for lifetime 'a) to a function which returns a slice (also of lifetime 'a). If those lifetimes are incompatible, then it won't typecheck. Consider creating two separate lifetimes.
pub struct Params<'a, 'b> {
pub signer: Option<&'a fn(address: &[u8; 20], data: &[u8; 32]) -> &'b[u8; 32]>,
}
Though, as you've already noted, the Fn trait is the better option. You seldom want to borrow an Fn, so I recommend that the type of signer be
pub signer: Option<Box<dyn Fn(address: &[u8; 20], data: &[u8; 32]) -> &'a [u8; 32]>>
Note the Box rather than &, and the keyword dyn which is recommended for trait objects in newer versions of Rust and will be required someday.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | |
| Solution 2 | Silvio Mayolo |
