Я много искал в Google, пытаясь понять это. У меня есть библиотека, которую я хотел бы, чтобы люди могли передать ссылку на пользовательскую функцию «подписанта» (в качестве альтернативы непосредственному предоставлению секретного ключа). Это самая последняя проблема, с которой я столкнулся:
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 и перепроектировать свои структуры и функции вокруг этого, но продолжаю застревать там, где мне нужно включить черту в конечную функцию (я получаю ошибки, что черта не определена).
Но нужно ли мне идти в этом направлении и сосредоточиться на выяснении того, почему эта целевая функция не распознает имя типажа? Такое ощущение, что использование 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>> {
. . .
}
Это прекрасно компилируется (безумно выглядящие «для» вещи были предложены и добавлены моей 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`
Я пробовал различные перестановки включения этого типа в область видимости (например, включая эту длинную строку «для» в определении функции-контейнера в этом блоке 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
Я думаю, что это будет хорошо работать для меня в данный момент. Спасибо за помощь!
Проблема в том, что вы связываете вместе слишком много жизней.
pub signer: Option<&'a fn(address: &[u8; 20], data: &[u8; 32]) -> &'a[u8; 32]>
Для этого требуется ссылка (которая существует на всю жизнь 'a
) на функцию, которая возвращает срез (также на всю жизнь '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 и когда-нибудь понадобится.
Проблема с вашим первоначальным кодом заключается в том, что, хотя вы можете привести (или принудить) элемент 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]
.
Хорошо, спасибо! Это все имеет смысл для меня. Я обновлю вопрос выше, указав, где я оказался.
Спасибо за ответ! Я сделал эти настройки, но, похоже, это не влияет на ошибку «ожидаемый указатель fn, но найденный элемент fn». Я поработаю над переходом на черту Fn и разберусь с проблемами там.