Зачем вам когда-либо использовать одни и те же времена жизни для ссылок в структуре?

Этот вопрос похож на вопрос " Когда полезно определить несколько времен жизни в структуре?"., но, надеюсь, достаточно разные. Ответ на этот вопрос полезен, но фокусируется на преимуществах одного подхода (использование отдельных времен жизни для ссылок в структуре), но не на недостатках (если таковые имеются). Этот вопрос, как этот, ищет руководство о том, как выбрать время жизни при создании структур.

Назовите эту версию вместе, потому что x и y должны иметь одинаковое время жизни:

struct Foo<'a> {
    x: &'a i32,
    y: &'a i32,
}

и назовите это свободной версией, потому что время жизни может варьироваться:

struct Foo<'a, 'b> {
    x: &'a i32,
    y: &'b i32,
}

Ответ на упомянутый вопрос дает ясный случай, когда клиентский код может компилироваться / запускаться, учитывая свободную версию, но не удастся для связанной версии. Разве это не тот случай, когда любой клиентский код, который работает для связанной версии, также будет работать для свободной версии и будет гарантированно таким же безопасным (то есть безопасным)? Аверс не верен. Свободная версия явно более гибкая с точки зрения структурного дизайнера. Учитывая, что это хороший / принятый ответ, руководство может быть таким: при использовании ссылок в структуре всегда дайте им различное время жизни.

В чем недостаток этого совета, игнорируя лишний набор текста? Например, есть ли преимущество в том, чтобы ссылки в структуре имели одинаковое время жизни?

1 ответ

Решение

есть ли польза от того, что ссылки в структуре имеют одинаковое время жизни?

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

fn foo<'a, 'b>(a: &'a str, b: &'b str) -> &str { // What lifetime to return?
    if (global_random_number() == 42) { a } else { b }
}

Применяя к структуре, вы можете получить что-то вроде этого:

struct EvenOrOdd<'a, 'b> {
    even: &'a str,
    odd: &'b str,
}

impl<'a, 'b> EvenOrOdd<'a, 'b> {
    fn do_it(&self, i: u8) -> &str {
        if i % 2 == 0 {
            self.even
        } else {
            self.odd
        }
    }
}

Обратите внимание, что хотя он компилируется, он не возвращает строку, которая может пережить саму структуру, а это не то, что предполагалось. Этот код не работает, хотя он должен работать:

fn foo<'a, 'b>(a: &'a str, b: &'b str) {
    let result = {
        EvenOrOdd { even: a, odd: b }.do_it(42)
    };

    println!("{}", result);
}

Это будет работать с единым временем жизни:

struct EvenOrOdd<'a> {
    even: &'a str,
    odd: &'a str,
}

impl<'a> EvenOrOdd<'a> {
    fn do_it(&self, i: u8) -> &'a str {
        if i % 2 == 0 { self.even } else { self.odd }
    }
}

Это противоположно связанному ответу, в котором есть комментарий:

Вы хотите иметь возможность получить совокупное значение и разделить его части после использования

В этом случае мы хотим взять совокупное значение и объединить их.

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

struct EvenOrOdd<'a, 'b: 'a> {
    even: &'a str,
    odd: &'b str,
}

impl<'a, 'b> EvenOrOdd<'a, 'b> {
    fn do_it(&self, i: u8) -> &'a str {
        if i % 2 == 0 { self.even } else { self.odd }
    }
}

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


игнорируя лишний ввод

Я бы не стал. имеющий

foo<'a>(Bar<'a>)

определенно лучше чем

foo<'a, 'b', 'c, 'd>(Bar<'a, 'b', 'c, 'd>)

Когда вы не пользуетесь дополнительными общими параметрами.

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