Спасаю из бездны ruRust/easy гиттера, вдруг кому пригодится в будущем:
Nikolay Govorov @nikolay-govorov:
Где я не прав? Почему env_path будет очищен раньше чем я сформирую структуру?pub struct Shell<'a> { os_path: Vec<&'a str>, } impl<'a> Shell<'a> { pub fn new() -> Shell<'a> { let env_path = env::var_os("PATH").unwrap().into_string().unwrap(); let os_path: Vec<&'a str> = env_path.as_str().split(':').collect(); Shell { os_path } } }
Ilya Bogdanov @vitvakatu:
Потому что ты берешь as_str, который возвращает &str, а затем разбиваешь этот &str на &'a str. Ну сам то подумай, у тебя из функции выходит Shell<'a>, а на входе никакого лайфтайма нет - значит ты возвращаешь структуру, лайфтаймом связанную с чем-то внутри тела функции (это можно выявить глядя только на сигнатуру)
Nikolay Govorov @nikolay-govorov:
@vitvakatu, а какое решение? Я должен вернуть что-то связанное с внешним лайфтаймом, но как?pub struct Shell<'a> { os_path: Vec<&'a str>, } impl<'a> Shell<'a> { pub fn new() -> Shell<'a> { let env_path = env::var_os("PATH").unwrap().into_string().unwrap(); let os_path: Vec<&str> = env_path.split(':').collect(); Shell { os_path } } }
Ilya Bogdanov @vitvakatu:
@nikolay-govorov Rust Playground
Alexander Irbis @irbis:
@nikolay-govorov в таких ситуациях есть два подхода.Самый простой и дорогой: можно создавать владеющие ссылки (Box, Rc, Arc,
Gc) - это иногда неизбежное, но достаточно дорогое решение, особенно, если нужно больше одного выделения памяти. Однако это вполне приемлемо, если это одноразовая операция и результат в дальнейшем многократно переиспользуется. Например, на старте программы подготавливаешь конфигурацию, а потом программа минутами/часами/днями работает.Либо можно передать в функцию заранее выделенный контейнер под результат, тогда у пользователя функции появляется возможность по желанию переиспользовать однажды выделенную память - так поступают функции ввода-вывода в стандартной библиотеке.
Ккроме того, как более продвинутый подход: можно сделать специальную структуру, которая будет хранить всё значение в сыром виде в одной переменной и отдельно индексы нужных фрагментов, которые будет превращать в ссылки, когда нужно получить к ним доступ - это работает, когда у тебя редкомодифицируемые данные, используется, например, в крейте url.
Ещё можно заметить, что может оказаться дешевле много раз повторить поиск по данным, чем один раз выделить память под все результаты. Если необходимое количество поисков тебе выгоднее, чем выделение памяти, то возможно стоит применять итератор, который даст возможность пройтись по всем результатам, а не собирать результаты в отдельную коллекцию.
Что я имею в виду под “выделение памяти - дорого”: обычно выделение памяти требует захвата мьютекса (возможно даже не одного, зависит от того, как устроен аллокатор) - это выливается в несколько микросекунд на современных процессорах. Проход по нескольким сотням байтов с поиском вполне может укладываться в несколько наносекунд. То есть, разница по времени примерно в тысячу раз.
Однако, если время работы программы не жмёт, нет высокой нагрузки, требующей выжимать всё из железа, - вероятно нет смысла изощряться и делать что-то сложнее самого наивного первого варианта. Впрочем, сделать итератор - тоже несложно.