Lifetime для указателей

Всем привет,

С удивлением узнал что у указателей бывает что-то вроде лафтаймов. Раньше я думал что стоит только только попросить у бокса указатель, например через Box::into_raw(boxed) как дальше можно делать с этим указателем что угодно, хоть отправить на луну и оттуда считать его лазером, главное чтобы когда ты решил этот указатель разыменовать или превратить обратно в коробочку через Box::from_raw(ptr) тебе надо поставить ключевое слово unsafe и далее все на твоей совести.

Здесь я правда имею в виду trait objects. Вот мой код который не компилируется:

trait Foo {}
struct Bar<'a> { name: &'a str }
impl<'a> Foo for Bar<'a> {}

fn get_ptr<'a>(name: &'a str) -> *mut dyn Foo { // <-- указатель захотел?
    let boxed = Box::new ( Bar { name } );
    Box::leak(boxed) // <-- а вот и нет
}


fn main() {
    let owned = "John".to_string();
    let ptr = get_ptr(&owned);

    println!("Hello {:?}", ptr);
}

Вот что я получаю:

error[E0759]: cannot infer an appropriate lifetime
 --> src/main.rs:8:28
  |
7 | fn get_ptr<'a>(name: &'a str) -> *mut dyn Foo {
  |                      ------- this data with lifetime `'a`...
8 |     let boxed = Box::new ( Bar { name } );
  |                            ^^^   ---- ...is captured here...
9 |     Box::leak(boxed)
  |     ---------------- ...and is required to live as long as `'static` here
  |
help: to declare that the trait object captures data from argument `name`, you can add an explicit `'a` lifetime bound
  |
7 | fn get_ptr<'a>(name: &'a str) -> *mut dyn Foo + 'a {
  |                                               ^^^^

error: aborting due to previous error

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

fn get_ptr<'a>(name: &'a str) -> *mut (dyn Foo + 'a) {

Такой подставы я не ожидал. Получается мне нужно как-то синтаксически доказывать компилятору что мой указатель не переживет тех ссылок которые есть у объекта на который он ссылается, что в моем случае затруднительно (если бы я мог, я бы не стал париться с указателями).

Может кто-то знает где можно про почитать про такое?

Как быть если нужен старый добрый указатель без borrowchk? Что-то вроде transmute *mut (dyn Foo + 'a) в *mut (dyn Foo + 'static)?

Этот лайфтайм прилагается не к указателю, а к dyn Trait.

Когда-то давным-давно его нужно было всегда указывать явно, то есть нельзя было написать, например, Box<Trait>, только Box<Trait + 'static> (это было ещё до введения синтаксиса dyn Trait).
Затем с принятием RFC 599 по умолчанию в его качестве стало использоваться или 'static (в случае владения), или лайфтайм ближайшей ссылки (в случае заимствования).
Затем, начиная с версии Rust 1.2 / 1.3, эти правила слегка скорректированы в RFC 1156: по умолчанию лайфтайм берётся с непосредственного контейнера, даже если выше были ссылки (то есть, например, &'a Box<Trait> - это &'a Box<Trait + 'static>, хотя раньше это было бы &'a Box<Trait + 'a>. Поподробнее можно почитать в главе Lifetime elision референса или по ссылкам на RFC выше.

А в чём заключается затруднение? Возможно, компилятор не зря предлагает повнимательнее посмотреть на это? Особенно если память “утекает” с помощью Box::leak… возможно, правильнее было бы просто потребовать name: &'static str?

Прикольная инфа, спасибо! Еще и с исторической перспективой.

Компилятор конечно все правильно говорит, но я пока не знаю как выразить все чтобы ему нравилось. Мой код где я столкнулся с проблемой немного другой. Это высоко экспериментальный экзекьютор выглядит как-то так:

async fn async_main<'runtime>(rt: &'runtime Runtime) {
    //...
    rt.spawn(another_async_fn(rt));
    //...
}

fn main() {
    start_with_runtime(async_main);
}

Я делаю реализацию Runtime.spawn(&'runtime self, future :Future + 'runtime)

Короче все фьючи которые создаются - все создаются с лайтаймом 'runtime и в общем случае их надо хранить где-нибудь в box (поскольку spawn может вызваться в цикле, например). spawn проверяет что lifetime у футуры 'runtime, но когда начинает заводить всякие Waker - становится сложно :slight_smile: Насколько я понимаю какой-нибудь токио просто просит чтобы все фучи были 'static, однако мне не нужно ничего такого, они все гарантированно освободятся когда start_with_runtime() завершиться.

transmute кстати хорошо помогает пока :slight_smile: