Как преобразовать Vec<String> в Vec<&str> в Rust?
Использование iter() и map() с преобразованием as_str()
Для преобразования Vec<String> в Vec<&str> нужно взять ссылки на строки, но не на сами String, а на их внутреннее содержимое. В Rust для этого используется метод as_str() у String, который возвращает &str. Вот идиоматический способ:
fn main() {
let owned_strings: Vec<String> = vec!["hello".to_string(), "world".to_string()];
// Преобразуем Vec<String> в Vec<&str>
let str_refs: Vec<&str> = owned_strings.iter()
.map(|s| s.as_str())
.collect();
process_strings(str_refs);
}
fn process_strings(strings: Vec<&str>) {
for s in strings {
println!("{}", s);
}
}
Объяснение шаг за шагом
iter()— создает итератор, который возвращает ссылки на элементы вектора, т.е.&String.map(|s| s.as_str())— для каждого&Stringвызывает методas_str(), который возвращает&str— срез строки, указывающий на те же данные.collect()— собирает все элементы в новыйVec<&str>.
Важный нюанс: время жизни
Обратите внимание, что полученные &str ссылаются на данные исходного Vec<String>. Это означает, что исходный вектор должен жить как минимум столько же, сколько и результирующий вектор ссылок. Если вы попытаетесь использовать Vec<&str> после удаления исходного вектора, компилятор выдаст ошибку времени жизни.
fn main() {
let str_refs;
{
let owned_strings = vec!["hello".to_string(), "world".to_string()];
str_refs = owned_strings.iter().map(|s| s.as_str()).collect();
// owned_strings удаляется здесь
}
// Ошибка! str_refs ссылается на уже удаленные данные
// process_strings(str_refs);
}
Альтернативный вариант: заимствование исходного вектора
Если вам не нужно владеть результирующим вектором, можно передавать ссылку на исходный вектор и преобразовывать на лету:
fn process_strings(strings: &[&str]) {
for s in strings {
println!("{}", s);
}
}
fn main() {
let owned_strings: Vec<String> = vec!["hello".to_string(), "world".to_string()];
let str_refs: Vec<&str> = owned_strings.iter().map(String::as_str).collect();
process_strings(&str_refs);
}
Или еще проще — использовать as_slice() и передавать срез:
fn process_strings(strings: &[&str]) {
// ...
}
fn main() {
let owned_strings = vec!["hello".to_string(), "world".to_string()];
let refs: Vec<&str> = owned_strings.iter().map(|s| s.as_str()).collect();
process_strings(&refs);
}
Почему не работает просто .iter().collect()?
Если вы напишете owned_strings.iter().collect(), то получите Vec<&String>, а не Vec<&str>. Это потому, что iter() возвращает ссылки на сами элементы вектора (&String), а не на их внутренние данные. Вам нужно явно "разыменовать" String до &str с помощью as_str() или через Deref (например, |s| &**s), но as_str() — самый читаемый вариант.
Использование Vec::from_iter и замыкания
Можно также применить более функциональный подход с Vec::from_iter, который иногда короче:
fn main() {
let owned: Vec<String> = vec!["foo".to_string(), "bar".to_string()];
let refs: Vec<&str> = Vec::from_iter(owned.iter().map(String::as_str));
// или даже так, если owned больше не нужен:
// let refs: Vec<&str> = owned.iter().map(|s| s.as_str()).collect();
println!("{:?}", refs);
}
Если нужно преобразовать и забыть об исходном векторе
Если вы больше не планируете использовать исходный Vec<String>, можно преобразовать его в Vec<&str> с помощью leak() (но это редко нужно и требует осторожности):
fn main() {
let owned = vec!["hello".to_string(), "world".to_string()];
let refs: Vec<&str> = owned.iter().map(|s| s.as_str()).collect();
// owned все еще жив, но refs на него ссылается
}
Краткое резюме
- Используйте
iter().map(|s| s.as_str()).collect()— это стандартный идиоматический способ. - Помните о времени жизни: исходный вектор должен пережить результирующий.
- Для простой передачи в функцию рассмотрите возможность изменения сигнатуры на
&[&str].
Ответов пока нет.
Войдите чтобы оставить ответ