При попытке решить такие проблемы, связанные со временем жизни, полезно составить диаграмму всех вовлеченных времен жизни. Есть 2 названных времени жизни, но минимум 5 соответствующих времен жизни. Это только для функции SliceHolder::foo
.
'static
. Это время жизни, которое будет иметь &DEFAULT
(в связи с продвижением 'static
). 'a
. Время жизни привязано к типу self
. 'b
. Время жизни среза. - Анонимное время жизни 1 (я назову это
'c
). Время жизни &mut
заимствовано на self
. - Анонимное время жизни 2 (назову это
'd
). Время жизни, соответствующее телу функции.
Вот схема их взаимоотношений (простите за дерьмовый ASCII-арт). Время жизни выше на диаграмме обычно длится дольше, но время жизни длится дольше, только если есть последовательность стрелок, соединяющих их.
'static
^
/ \
/ \
/ \
| |
V V
'a 'b
| |
V |
'c |
\ /
\ /
\ /
V
'd
Добавление ограничения времени жизни 'b: 'a
равносильно добавлению стрелки 'a -> 'b
на эта диаграмма, говорящая о том, что 'a
переживает 'b
или заимствует с временем жизни 'a
, действительна для всего периода 'b
.
Давайте посмотрим, как эти времена жизни взаимодействуют в main
.
// holder: SliceHolder<'a>, where 'a: 'static
let mut holder = SliceHolder { slice: &DEFAULT };
let mut x: [u8; 1] = [1];
// Take a mutable borrow of holder (with lifetime 'c1)
// Also take a borrow of x with lifetime 'b1
holder.foo(&x);
// mutate x. This forces 'b1 to end no later than here.
x[0] = 5;
// Take another mutable borrow of holder ('c2)
// and another borrow of x ('b2)
holder.foo(&x);
Это показывает, что у нас не может быть 'b: 'a
, поскольку это будет означать, что 'a
должен закончиться не позднее, чем когда 'b
закончится. Это заставит holder
прекратить существование до того, как x
будет мутирован. Но holder
используется позже этого.
Итак, мы показали, что у нас не может быть 'b: 'a
. Что тогда осталось? Когда мы выполняем присваивание self.slice = slice;
, мы неявно приводим значение от &'b [u8]
к &'a [u8]
. Для этого требуется 'b: 'a
, что мы только что исключили. self
всегда должен иметь тип SliceHolder<'a>
, поэтому мы не можем просто сократить его время жизни.
В сторону: если мы не поддержали это, то срез в self
всегда имеет время жизни (по крайней мере) 'a
, мы могли бы в какой-то момент (* например, в do_something_with_holder
) преобразовать c и избежать контрольного пути, где self.slice
переназначается чему-либо с более длинным временем жизни. 'b
закончится (аннулирует slice: &'b [u8]
), но self
все еще будет существовать и будет содержать недопустимую ссылку. Это то, что вам нужно учитывать, если вы когда-либо используете код unsafe
.
Однако у нас может быть вторая переменная с более коротким временем жизни, чье значение (т.е. внутренний срез) совпадает с self
. Нам нужно, чтобы эта вторая переменная имела тип SliceHolder<'_>
с некоторым временем жизни, не превышающим 'a
(чтобы мы могли использовать ссылку self
) и не длиннее 'b
( поэтому мы можем присвоить slice
его срезу). Мы смотрим на диаграмму и видим, что время жизни действительно меньше, чем у 'a
и 'b
, а именно 'd
.
К счастью, нам не нужно беспокоиться об именовании этого времени жизни. Все, что имеет значение, так это то, что он существует, а остальное компилятор выяснит. Итак, как мы можем получить эту вторую переменную? Нам нужно как-то переместить self
в новую переменную. Но помните, мы не можем выйти из изменчивых ссылок, поэтому мы должны оставить внутри что-то действительное.
Мы уже планировали отключить ссылку self
для &DEFAULT
, так почему бы не просто сделай это? Соответствующая команда - std::mem::replace
и может использоваться как let second_variable = std::mem::replace(self, SliceHolder {slice: &DEFAULT})
.
. Есть несколько более эргономичных c способов сделать это, но это потребует добавления некоторых черт для SliceHolder
. Если SliceHolder
действительно содержит только ссылку, он может реализовать Copy
, что делает все в этой ситуации проще. За исключением этого, реализация Default
для него (с &DEFAULT
в качестве среза по умолчанию) позволит вам использовать вновь стабилизированный std::mem::take
вместо std::mem::replace
. Тогда вам не нужно будет создавать это значение по умолчанию inline.
Возможно, есть некоторые другие вещи, которые необходимо учитывать, но без минимального воспроизводимого примера трудно сказать, что. Я оставлю вас с некоторым рабочим кодом (детская площадка) .
struct SliceHolder<'a> {
slice: &'a [u8],
}
const DEFAULT: [u8; 0] = [];
struct Result;
fn do_something_with_holder(_: &SliceHolder) -> Result {
Result
}
impl<'a> SliceHolder<'a> {
fn foo<'b>(&mut self, slice: &'b [u8]) -> Result {
// new_holder has the exact value that self would have had before
let mut new_holder: SliceHolder<'_> =
std::mem::replace(self, SliceHolder { slice: &DEFAULT });
new_holder.slice = slice;
let result = do_something_with_holder(&new_holder);
// self.slice = &DEFAULT; // no longer needed - we've already taken care of that
result
}
}
fn main() {
let mut holder = SliceHolder { slice: &DEFAULT };
let mut x: [u8; 1] = [1];
holder.foo(&x);
x[0] = 5;
holder.foo(&x);
}