Обновить
Комментарии 18
Строка же это utf8, то есть набор байтов.
Это понятно. as_bytes() как раз и вернет нам массив байтов — &[u8] . Вопрос в том, что вы с ними дальше будете делать, чтобы получить str?
Про конкатенацию строк.
Я с растом мало имел дел, тонкостей могу не знать. Поискал, предлагают конкатенацию делать через интерполяцию с макро format!.. Это не то?
А можете ссылкой кинуть? Или детали написать? (например конкретный пример кода)
На самом деле преобразование String в &str делается очень просто. Во-первых, String реализует Deref<Target=str>, поэтому достаточно просто взять адрес от разыменованной строки или положиться на deref coercion, если целевой тип известен:
let s: String = "hello world".into();
let ss = &*s;  // ss: &str

let ss: &str = &s;  // deref coercion, потому что тип известен

А поскольку String реализует Deref<Target=str>, а str реализует трейт Index<Range>, то можно воспользоваться slicing syntax:
let ss = &s[..];

Хотя использование deref coercion более идиоматично.
Очень плохо, что в документации as_str() не написано, почему он unstable. Он таков, потому что, вполне вероятно, он будет задепрекейчен. Вероятно, имеет смысл создать ишью в трекере Rust на эту тему.
И ещё, to_string() не рекомендуется использовать для преобразования &str в String потому что он несёт накладные расходы — он использует механизм std::fmt вместо того, чтобы напрямую создать новую строку и скопировать байты. Поэтому лучше использовать to_owned() (если целевой тип неизвестен) или into() (если известен).
И последнее — поиск в гугле по запросу «rust string to str» в первых ссылках даёт вот этот вопрос и ответ на stackoverflow, где объясняются все вышеописанные варианты (хоть и название вопроса немного не то).
Спасибо. Как-то я проглядел эту ссылку…
Для чего вам вообще склеивать str? Он для этого не предназначен. Его можно сравнить с Сишным char * — просто немодифицируемая последовательность символов.

String предназначен для манипуляции строками. Более того, вам даже он не нужен.

Вы хотите передавать путь до файла в функцию — так и принимайте его! Для манипуляции Path есть тонна удобнейших функций.

std::path::Path usage
use std::path::Path;

struct Model {
    u: f32,
}

impl Model {
    fn new(file_path: &Path) -> Model {
        let texture_path = file_path.with_extension("tga");

        println!("path: {:?}", texture_path);
        // outputs 'path: "/tmp/models/african_head.tga"'
        Model{ u: 1.0 }
    }
}

fn main() {
    let model = Model::new(Path::new("/tmp/models/african_head.obj"));
}


gist mirror
Мне не просто расширение поменять надо. Еще к имени файла прибавить суффикс "_diffuse". Когда писал это дело, я искал, как прибавить суффикс к имени файла средствами std::path::Path. Не нашел. Может плохо искал?
А, теперь я понял, для чего вам конкатенация.

Тогда вот вариант с использованием format! и PathBuf (на gist тоже обновил, ссылка выше):

Скрытый текст
use std::path::{Path, PathBuf};

struct Model {
    u: f32,
}

fn texture_fname(path: &Path) -> PathBuf {
    let fullname = format!("{}{}", path.file_stem().unwrap().to_str().unwrap(), "_diffuse");

    let mut buf = PathBuf::from(path);
    buf.set_file_name(fullname);
    buf.set_extension("tga");
    buf
}

impl Model {
    fn new(texture_path: &Path) -> Model {
        let texture_path = texture_fname(texture_path);

        println!("path: {:?}", texture_path.as_path());
        // outputs 'path: "/tmp/models/african_head_diffuse.tga"'
        Model{ u: 1.0 }
    }
}

fn main() {
    let model = Model::new(Path::new("/tmp/models/african_head.obj"));
}


Вот здесь ещё несколько вариантов: stackoverflow.com/a/30481077/1145239

Я бы на вашем месте просто сменил расширение или складывал в другую директорию.
Вот вариант без функции, наиболее близок к вашему коду:
Скрытый текст
use std::path::{Path, PathBuf};

struct Model {
    u: f32,
}

impl Model {
    fn new(texture_path: &str) -> Model {
        let buf = PathBuf::from(texture_path);
        let texture_path = format!("{}/{}{}",
                                   buf.parent().unwrap().to_str().unwrap(),
                                   buf.file_stem().unwrap().to_str().unwrap(),
                                   "_diffuse.tga");

        println!("path: {:?}", texture_path);
        // outputs 'path: "/tmp/models/african_head_diffuse.tga"'
        Model{ u: 1.0 }
    }
}

fn main() {
    let model = Model::new("/tmp/models/african_head.obj");
}


На мой взгляд, здесь несколько недостатков:

  • Используются непортируемые разделители директорий /
  • Менее очевидно, что в Model::new передаётся именно путь до файла
  • Меньшая гибкость — когда генерация имени вынесена в отдельную функцию, её легче повторно использовать и менять.
А почему
format!("{}{}", path.file_stem().unwrap().to_str().unwrap(), "_diffuse");
а не
format!("{}_diffuse", path.file_stem().unwrap().to_str().unwrap());


Да, так тоже можно, и выглядит проще.
Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.