如何将String类型的Vector转换为[&str]切片?
Rust中Vec适配gtk::DropDown::from_strings的问题
问题场景
当使用字符串字面量构建Vec<&str>并传入gtk::DropDown::from_strings时一切正常,但动态构建Vec<String>后传入该方法会报错:
// 运行正常 let imports = vec!["import 1", "import 2"]; let import_selector = gtk::DropDown::from_strings(&imports); // 报错代码 let mut imports = Vec::new(); for imp in app_settings.lock().unwrap().imports.clone() { let name = match imp.clone() { Some(name) => name, None => continue }; if !imports.contains(&name) { imports.push(name.as_str()); } } let import_selector = gtk::DropDown::from_strings(&imports); // 报错信息:expected slice `[&str]`, found struct `Vec`
复现代码(可运行):
use gtk::prelude::*; use gtk::{Orientation}; use libadwaita::{Application, ApplicationWindow}; const APP_ID: &str = "org.gtk_rs.HelloWorld2"; fn main() { let app = Application::builder().application_id(APP_ID).build(); app.connect_activate(build_ui); app.run(); } pub fn build_ui(app: &Application) { let imports = vec!["import 1", "import 2"]; let import_selector = gtk::DropDown::from_strings(&imports); let mut imports_as_strings = Vec::new(); imports_as_strings.push(String::from("import 1")); imports_as_strings.push(String::from("import 2")); // 报错部分 let mut imports_2 = Vec::new(); for imp in imports_as_strings { if !imports_2.contains(&imp) { imports_2.push(imp); } } let import_2_selector = gtk::DropDown::from_strings(&imports_2); // 报错:expected slice `[&str]`, found struct `Vec` let new_box = gtk::Box::builder() .orientation(Orientation::Vertical) .build(); new_box.append(&import_selector); new_box.append(&import_2_selector); let window = ApplicationWindow::builder() .application(app) .title("My GTK App") .build(); window.present(); }
问题原因
gtk::DropDown::from_strings的参数类型是&[&str](即&str的切片),而你动态构建的imports_2是Vec<String>类型。Rust不会自动将Vec<String>隐式转换为&[&str],因为String是拥有所有权的字符串类型,&str是字符串切片,二者类型不同,需要显式转换。
另外,你尝试直接push(name.as_str())到Vec<&str>时,还会遇到生命周期问题:name是循环内的局部变量,循环结束后name会被销毁,导致&str引用悬空,这也是你之前指定Vec<&str>无效的原因。
解决方法
方法1:将Vec转换为Vec<&str>后传入
在调用from_strings时,将Vec<String>中的每个元素转换为&str,收集成临时的Vec<&str>,再传入其引用。这种方法简单直接,且能保证引用的生命周期足够:
// 修改报错部分的代码 let mut imports_2 = Vec::new(); // 改为引用遍历,避免消费imports_as_strings,保证后续&str的生命周期 for imp in &imports_as_strings { if !imports_2.contains(imp) { imports_2.push(imp.as_str()); } } // 此时imports_2是Vec<&str>,可以直接传入 let import_2_selector = gtk::DropDown::from_strings(&imports_2);
或者更简洁的写法,直接在调用时转换:
// 跳过单独构建imports_2的步骤,直接转换并传入 let import_2_selector = gtk::DropDown::from_strings( &imports_as_strings.iter().map(|s| s.as_str()).collect::<Vec<_>>() );
方法2:使用拥有所有权的字符串集合(推荐)
如果场景允许,也可以使用gtk::StringList替代手动构建Vec,它是GTK提供的拥有所有权的字符串列表类型,更适配GTK的API:
use gtk::StringList; // 替换报错部分的代码 let string_list = StringList::new(&[]); for imp in imports_as_strings { if !string_list.contains(&imp) { string_list.append(&imp); } } let import_2_selector = gtk::DropDown::new(Some(&string_list), None);
这种方法无需处理引用生命周期问题,更符合GTK Rust绑定的使用习惯。
内容的提问来源于stack exchange,提问作者John554
相关产品推荐
相关产品推荐

