Зачем вам когда-либо использовать одни и те же времена жизни для ссылок в структуре?
Этот вопрос похож на вопрос " Когда полезно определить несколько времен жизни в структуре?"., но, надеюсь, достаточно разные. Ответ на этот вопрос полезен, но фокусируется на преимуществах одного подхода (использование отдельных времен жизни для ссылок в структуре), но не на недостатках (если таковые имеются). Этот вопрос, как этот, ищет руководство о том, как выбрать время жизни при создании структур.
Назовите эту версию вместе, потому что 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>)
Когда вы не пользуетесь дополнительными общими параметрами.