Универсальный параметр со ссылкой, используемой в качестве аргумента указателя функции

#rust

Вопрос:

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

 struct CB<Data> {
    cb: fn(Data) -> usize
}
 

Я хотел бы сохранить экземпляр этого, параметризованный каким-либо типом, содержащим ссылку, в какой-либо другой структуре, которая реализует признак с помощью одного метода, и использовать этот метод признака для вызова указателя функции в CB.

 struct Holder<'a> {
    c: CB<Option<amp;'a usize>>
}
trait Exec {
    fn exec(amp;self, v: amp;usize) -> usize;
}
impl<'a> Holder<'a> {
    fn exec_aux(amp;self, v: amp;'a usize) -> usize {
        (self.c.cb)(Some(v))
    }
}
impl<'a> Exec for Holder<'a> {
    fn exec(amp;self, v: amp;usize) -> usize
    {
        self.exec_aux(v)
    }
}
 

Это дает мне пожизненную ошибку для включения «Exec» держателя:

 error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'a` due to conflicting requirements
 

Простой вызов exec_aux работает нормально, пока я не определяю это Exec значение:

 fn main() {
    let h = Holder { c: CB{cb:cbf}};
    let v = 12;
    println!("{}", h.exec_aux(amp;v));
}
 

Кроме того, то, что CB не является универсальным, также делает эту работу:

 struct CB {
    cb: fn(Option<amp;usize>) -> usize
}
 

Параметр в моем фактическом коде-это не usize что-то большое, что я бы предпочел не копировать.

Ответ №1:

Продолжительность жизни в вашей Exec черте неявно такова:

 trait Exec {
    fn exec<'s, 'a>(amp;'s self, v: amp;'a usize) -> usize;
}
 

Другими словами, типы, которые реализуют Exec , должны принимать любые периоды жизни 's и 'a . Однако ваш Holder::exec_aux метод ожидает определенного времени жизни 'a , связанного с параметром времени жизни Holder типа.

Чтобы это сработало, вам нужно вместо этого добавить в 'a качестве параметра срок Exec службы признак, чтобы вы могли реализовать признак специально для этого срока службы:

 trait Exec<'a> {
//        ^^^^         vv
    fn exec(amp;self, v: amp;'a usize) -> usize;
}

impl<'a> Exec<'a> for Holder<'a> {
//           ^^^^      vv
    fn exec(amp;self, v: amp;'a usize) -> usize
    {
        self.exec_aux(v)
    }
}
 

Комментарии:

1. Спасибо. Так будет ли считаться дурным тоном, скажем, в библиотеке/ящике, делать эти неявные периоды жизни явными для новых черт, даже если я не уверен, что они будут реализованы для чего-то подобного?

Ответ №2:

Проблема здесь в том, что Exec признак слишком общий, чтобы его можно было использовать таким образом Holder . Во-первых, рассмотрим определение:

 trait Exec {
    fn exec(amp;self, v: amp;usize) -> usize;
}
 

Это определение приведет к тому, что компилятор автоматически назначит два анонимных срока службы для amp;self и amp;v в exec . Это в основном то же самое, что

 fn exec<'a, 'b>(amp;'a self, v: amp;'b usize) -> usize;
 

Обратите внимание, что нет никаких ограничений на то, кто кого должен пережить, ссылки просто должны быть живыми во время вызова метода.

Теперь рассмотрим определение

 impl<'a> Holder<'a> {
    fn exec_aux(amp;self, v: amp;'a usize) -> usize {
        // ... doesn't matter
    }
}
 

Поскольку мы знаем, что amp;self это a amp;Holder<'a> (это то, к чему impl относится), нам нужно иметь по крайней мере a amp;'a Holder<'a> здесь, потому amp;'_ self что не может быть жизни короче, чем 'a в Holder<'a> . Таким образом, это говорит о том, что два параметра имеют одинаковый срок службы: amp;'a self, amp;'a usize .

Где все идет не так, как надо, так это когда вы пытаетесь совместить то и другое. Эта черта вынуждает вас использовать следующую подпись, которая (опять же) имеет два различных неявных периода жизни. Но фактический Holder , для которого вы затем пытаетесь вызвать метод, заставляет вас иметь одинаковое время жизни для amp;self и amp;v .

 fn exec(amp;self, v: amp;usize) -> usize {
    // Holder<'a> needs `v` to be `'a` when calling exec_aux
    // But the trait doesn't say so.
    self.exec_aux(v)
}
 

Одним из решений является переопределение признака как

 trait Exec<'a> {
    fn exec(amp;'a self, v: amp;'a usize) -> usize;
}
 

а затем реализовать его как

 impl<'a> Exec<'a> for Holder<'a> {
    fn exec(amp;'a self, v: amp;'a usize) -> usize {
        self.exec_aux(v)
    }
}