Общий параметр, связанный другим параметром - PullRequest
0 голосов
/ 11 февраля 2019

Я пишу фальшивые рамки.Для этого мне нужно принять функцию, которую можно использовать вместо другой, и сохранить ее.Мой нынешний дизайн делает это путем форсирования одних и тех же типов ввода и вывода, но он не дает правильных значений времени жизни.

Вместо этого мне нужно написать универсальную функцию, которая принимает базовую функцию и ее заменитель:

fn main() {
    selector(foo, baz, "local", false);
    selector(bar, baz, "local", false);
    selector(foo, bar, "local", false); // SHOULD FAIL, bar is NOT substitute of foo
}

fn foo(_: &str) -> &'static str {
    "foo"
}
fn bar(s: &str) -> &str {
    s
}
fn baz(_: &str) -> &'static str {
    "baz"
}

// DOES NOT COMPILE
// fn selector<U, V, F: Fn(U) -> V, G: F>(base: F, subs: G, arg: U, use_base: bool) -> V {
//     match use_base {
//         true => base(arg),
//         false => subs(arg),
//     }
// }

// COMPILES, but is too weak
fn selector<U, V, F: Fn(U) -> V, G: Fn(U) -> V>(base: F, subs: G, arg: U, use_base: bool) -> V {
    match use_base {
        true => base(arg),
        false => subs(arg),
    }
}

Playground

By "заменитель"Я имею в виду функцию, которая принимает по крайней мере каждый аргумент, принятый базой, и для каждого из них возвращает значение, по крайней мере, пригодное для использования в любом месте, где значение, возвращаемое из базыНапример:

fn foo(_: &str) -> &'static str {
    "foo"
}
fn bar(s: &str) -> &str {
    s
}
fn baz(_: &str) -> &'static str {
    "baz"
}

baz является заменой foo и bar, поскольку возвращаемая строка может использоваться либо вместо 'static, либо зависеть от заимствования.bar является не заменой foo, потому что заимствованное значение не может использоваться вместо 'static.

Я хочу создать что-то подобное, ноон не компилируется:

//                            FAILURE
//                               V
fn selector<U, V, F: Fn(U) -> V, G: F>(base: F, subs: G) {...}

Проблема в том, что я не могу выразить связь между F и G.Кажется, что в ржавчине нет понятия супертипа конкретного типа.

1 Ответ

0 голосов
/ 11 февраля 2019

Rust также знает об этих видах "совместимости" типов: все это о подтипах и дисперсии .Хитрость заключается в том, чтобы просто сделать так, чтобы оба аргумента имели один и тот же тип, по крайней мере, что касается функции .

Давайте сначала попробуем что-нибудь попроще:

// Both arguments have the same type (including the same lifetime)
fn foo<'a>(x: &'a i32, y: &'a i32) -> &'a i32 { 
    x 
}

let outer = 3;
{
    let inner = 27;
    println!("{}", foo(&outer, &inner));
}

Почему это работает?outer и inner явно имеют разные времена жизни!Потому что при вызове функции Rust рассматривает подтип, чтобы посмотреть, можно ли вызвать функцию.В частности, тип &'outer i32 является подтипом &'inner i32, поэтому его можно без проблем превратить в &'inner i32 и вызвать функцию.


Применение этой идеи к вашей проблеме означает, что ваша функция имеет только один тип функции и оба аргумента (base и subs) имеют этот тип:

fn selector<U, V, F: Fn(U) -> V>(base: F, subs: F, arg: U, use_base: bool) -> V {
    match use_base {
        true => base(arg),
        false => subs(arg),
    }
}

Еслимы пытаемся так, но, к сожалению, все равно получаем ошибку: " ожидаемый элемент fn, найден другой элемент fn ".Это связано с проблемой «элемент функции и указатель на функцию».Вы можете узнать больше об этом в этом ответе или в справочнике Rust .Приведение к указателю на функцию, к сожалению, не срабатывает в этой ситуации, поэтому одним из способов было бы явное приведение функций:

type FnStatic = fn(&str) -> &'static str;
type FnWeaker = fn(&str) -> &str;

selector(foo as FnStatic, baz as FnStatic, "local", false);
selector(bar as FnWeaker, baz as FnStatic, "local", false);
selector(foo as FnStatic, bar as FnWeaker, "local", false); 

( Playground )

Это на самом деле работает, как и ожидалось: первые два вызова в порядке, но третьи ошибки:

error[E0308]: mismatched types
 --> src/main.rs:7:31
  |
7 |     selector(foo as FnStatic, bar as FnWeaker, "local", false); 
  |                               ^^^^^^^^^^^^^^^ expected concrete lifetime, found bound lifetime parameter
  |
  = note: expected type `for<'r> fn(&'r str) -> &str`
             found type `for<'r> fn(&'r str) -> &'r str`

Однако все же немного уродливо приводить типы функций явно на сайте вызова.К сожалению, я не нашел способ это скрыть.Я попытался написать макрос, который принуждал к принуждению, но это не работало, когда указатель на функцию имел разные типы (включая второй пример).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...