Search code examples
rustgtk-rs

How do I convert a Vector of Strings to [&str]


I have a vector of strings and a method that is accepting [&str]. When I provide the strings as string literals everything works however when I try to build the same vector dynamically using Strings everything is failing.

Examples below:

//everything works fine.
let imports = vec!["import 1", "import 2"];
let import_selector = gtk::DropDown::from_strings(&imports);

//Throws an error
let mut imports = Vec::new(); // ive tried specifying the type here as Vec<&str> but it didn't help.
    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()); //I've tried this as &name giving the same result.
        }
    }

let import_selector = gtk::DropDown::from_strings(&imports);
//throws error: expected slice `[&str]`, found struct `Vec`

I think this is something to do with how I am passing the strings into the vector as I am passing String and not a string literal however the Vector.push method seems to only accept String.

How do I go about fixing this?

Adding a working example to demonstrate this issue:

use gtk::prelude::*;
use gtk::{CssProvider, StyleContext, Orientation};
use gtk::gdk::Display;
use libadwaita::{Application, ApplicationWindow};
use std::path::{Path, PathBuf};
use std::thread;
use std::fs;
use std::time::Duration;

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 imports_as_strings = Vec::new();
    imports_as_strings.push(String::from("import 1"));
    imports_as_strings.push(String::from("import 2"));
    
    //Throws an error
    let mut imports_2 = Vec::new(); // ive tried specifying the type here as Vec<&str> but it didn't help.
        for imp in imports_as_strings {
            if !imports_2.contains(&imp) {
                imports_2.push(imp); //I've tried this as &name giving the same result.
            }
        }
    
    let import_2_selector = gtk::DropDown::from_strings(&imports_2);
    //throws error: 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();

    // Present window
    window.present();
}

Solution

  • I wasn't able to reproduce your issue, but I'm guessing you aren't actually trying this with as_str and instead adding a reference. When you do that, you create a Vec<&String> instead of a Vec<&str>. If you simply add the type on this line:

    let mut imports_2: Vec<&str> = Vec::new();
    

    then you can guarantee this Vec will always deref to &[&str] This will move your error elsewhere, or allow type inference to fix it.

    You can push &String or &str values and they will be deref-ed to &str. If you try to push String, you will be told to add a reference.

    imports.push(&name);
    

    Doing as_str should always work.

    imports.push(name.as_str());