处理"expected fn pointer, found fn item"



我已经做了很多谷歌搜索试图弄清楚这一点。我有一个库,我希望人们能够传递对自定义签名者的引用。函数(作为直接提供密钥的替代方案)。这是我最近面临的问题:


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

我正试图将该函数传递给像这样通过系统的struct:

pub struct Params<'a> {
pub signer: Option<&'a fn(address: &[u8; 20], data: &[u8; 32]) -> &'a[u8; 32]>,
}

我已经尝试过强制转换函数,我在大多数地方定义它,但编译器抱怨我不能强制转换非基本类型:

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

我试过沿着使用Fn trait的道路重新设计我的结构和函数,但一直被困在我需要在最终函数中包含trait的地方(我会得到trait未定义的错误)。

但这是我需要去的方向,并专注于弄清楚为什么目标函数不会识别特征名称?感觉像我现在这样使用fn是最直接的,指针和项是最直接的。这似乎是一件微不足道的事情,但我就是找不到。

编辑

好的,我已经切换回使用Fn特性并写入:

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>> {

. . . 
}

编译得很好(看起来很疯狂的"for"东西是由我的IDE建议和添加的),但是当我试图在另一个模块中使用SignaturesParams时,我遇到了问题:

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`

我已经尝试了将该类型纳入范围的各种排列(例如,包括长"for"在signatures_params块上的容器函数的定义行,但还没有找到正确的方法来识别它。

我显然有点笨手笨脚,我现在的结构不是最佳的——我只是想弄清楚它是如何连接在一起的。谢谢你的建议!

最终更新

我最终放弃了我试图工作的流程(即,在前面定义一个闭包,并通过几个结构体传递它,最终由终止函数执行)。相反,我将步骤分解成更小的部分,允许我独立地预构建消息,然后将这些消息和自定义闭包直接传递给Signer结构体。

#[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)
}
}

与之一起使用的闭包可以这样定义和使用。

let closure = async move |x: SignData| { 
. . .
Signature { data: some_String } 
};
let signature = Signer::new(closure).sign(some_SignData).await.data

我想目前这对我来说很合适。谢谢你的帮助!

你的初始代码的问题是,虽然你可以强制转换(或强制)一个fn项(编译器称之为fn ... {name})到fn指针(fn ...),这个属性不是递归的:你不能强制转换对fn项的引用到对fn指针的引用。也就是说,signer_func as fn(&[u8; 20], &[u8; 32]) -> &'a [u8; 32]工作,但&signer_func as &fn(&[u8; 20], &[u8; 32]) -> &'a [u8; 32]没有。修复方法很简单:强制转换,然后接受引用。即&(signer_func as fn(&[u8; 20], &[u8; 32]) -> &'a [u8; 32])

&fn()看起来根本不对:fn()已经是一个指针,所以&fn()是冗余的双重定向。

第二个代码中的问题是编译器无法推断CustomSigner的类型,因为您提供了None。您需要显式地指定此类型参数。如果不看代码,我无法判断它需要什么,但假设在这种情况下您并不真正需要它,您可以指定一个虚拟类型,例如fn(&SignRequest) -> [u8; 32]

问题是你将太多的生命周期绑定在一起。

pub signer: Option<&'a fn(address: &[u8; 20], data: &[u8; 32]) -> &'a[u8; 32]>

这需要一个引用(存在于生存期'a)到一个返回一个slice(生存期'a)的函数。如果这些生命周期不兼容,则不会进行类型检查。考虑创建两个独立的生命周期。

pub struct Params<'a, 'b> {
pub signer: Option<&'a fn(address: &[u8; 20], data: &[u8; 32]) -> &'b[u8; 32]>,
}

虽然,正如你已经注意到的,Fn特性是更好的选择。您很少想要借用Fn,所以我建议signer的类型为

pub signer: Option<Box<dyn Fn(address: &[u8; 20], data: &[u8; 32]) -> &'a [u8; 32]>>

注意Box而不是&,关键字dyn在新版本的Rust中被推荐用于trait对象,将来会用到。

最新更新