Работа с «ожидаемым указателем fn, найденным элементом fn»

Я много искал в 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

Я думаю, что это будет хорошо работать для меня в данный момент. Спасибо за помощь!

Почему Python в конце концов умрет
Почему Python в конце концов умрет
Последние 20 лет были действительно хорошими для Python. Он прошел путь от "просто языка сценариев" до основного языка, используемого для написания...
0
0
91
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Проблема в том, что вы связываете вместе слишком много жизней.

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». Я поработаю над переходом на черту Fn и разберусь с проблемами там.

Justin Thomas 23.04.2022 00:11
Ответ принят как подходящий

Проблема с вашим первоначальным кодом заключается в том, что, хотя вы можете привести (или принудить) элемент 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].

Хорошо, спасибо! Это все имеет смысл для меня. Я обновлю вопрос выше, указав, где я оказался.

Justin Thomas 26.04.2022 18:26

Другие вопросы по теме