Сопоставить строку → &str из аргументов программы

Я пытаюсь сопоставить аргументы программы, которые задаются как Vec of String, в Vec из &str. Здесь я использую итератор std::env::args():

let argv_as_refs: Vec<&str> = env::args().map(String::as_str).collect();

Я думаю, что это вопрос жизней. Мне нужно только argv_as_refs для перехода в другую функцию (другая рассматриваемая функция является частью CXX-библиотеки, поэтому ей нужны char**, а не объекты String).

Я попробовал код и получил эту ошибку:

error[E0631]: type mismatch in function arguments
  --> src/main.rs:28:51
   |
28 |     let argv_as_refs: Vec<&str> = env::args().map(String::as_str).collect();
   |                                               --- ^^^^^^^^^^^^^^
   |                                               |   |
   |                                               |   expected due to this
   |                                               |   found signature defined here
   |                                               required by a bound introduced by this call
   |
   = note: expected function signature `fn(String) -> _`
              found function signature `for<'r> fn(&'r String) -> _`
note: required by a bound in `map`

error[E0599]: the method `collect` exists for struct `Map<Args, for<'r> fn(&'r String) -> &'r str {String::as_str}>`, but its trait bounds were not satisfied
  --> src/main.rs:28:67
   |
28 |     let argv_as_refs: Vec<&str> = env::args().map(String::as_str).collect();
   |                                                                   ^^^^^^^ method cannot be called on `Map<Args, for<'r> fn(&'r String) -> &'r str {String::as_str}>` due to unsatisfied trait bounds
   |
   = note: the following trait bounds were not satisfied:
           `<for<'r> fn(&'r String) -> &'r str {String::as_str} as FnOnce<(String,)>>::Output = _`
           which is required by `Map<Args, for<'r> fn(&'r String) -> &'r str {String::as_str}>: Iterator`
           `for<'r> fn(&'r String) -> &'r str {String::as_str}: FnMut<(String,)>`
           which is required by `Map<Args, for<'r> fn(&'r String) -> &'r str {String::as_str}>: Iterator`
           `Map<Args, for<'r> fn(&'r String) -> &'r str {String::as_str}>: Iterator`
           which is required by `&mut Map<Args, for<'r> fn(&'r String) -> &'r str {String::as_str}>: Iterator`
Почему Python в конце концов умрет
Почему Python в конце концов умрет
Последние 20 лет были действительно хорошими для Python. Он прошел путь от "просто языка сценариев" до основного языка, используемого для написания...
0
0
62
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Тип Item итератора, возвращаемого env::args(), — String. Это означает, что в функции map значение было перемещено. Если вы перепишете это, чтобы использовать замыкание, возможно, будет легче увидеть, что происходит:

env::args()
    .map(|s| {
        // s is moved into this closure
        s.as_str()
        // s will be dropped here
    })
    .collect();

Значение, по которому выполняется итерация, будет удалено внутри функции сопоставления, поэтому функция сопоставления не сможет вернуть ссылку на него.

Вместо этого вы можете собрать в коллекцию String:

let args_owned: Vec<String> = env::args().collect();

Затем, если вам действительно нужен Vec<&str>, вы можете сделать ссылку на него:

let argv_as_refs: Vec<&str> = args_owned.iter().map(String::as_str).collect();

Теперь это работает так, как вы хотели, поскольку строки принадлежат включающей функции и могут быть заимствованы на этот период.

Ответ принят как подходящий

Здесь есть две проблемы:

  • Результатом args() является итератор, принадлежащий String. Поскольку вы .map() итератор, некому удерживать память, поддерживающую каждую строку; к тому времени, когда итератор будет исчерпан, строки исчезнут, поэтому &str будет в недопустимом состоянии; кто-то должен сохранить это воспоминание.
  • Помните, что String имеет кодировку UTF-8 и может содержать нулевые байты, чего ваш FFI может не ожидать. Не все String являются допустимыми строками типа C. Вам нужно будет совершить дополнительный обход CString, чтобы проверить и/или добавить необходимый нулевой байт в конец строки.
use std::env;
use std::ffi;

// it's pointers all the way down
fn bar(args: *const *const ffi::c_char) {}


fn main() {
    // Convert the strings to c-style strings, causing a panic
    // if those strings contain nul-bytes (as they could, being utf-8)
    let args = env::args()
        .map(ffi::CString::new)
        .collect::<Result<Vec<ffi::CString>, _>>()
        .unwrap();

    // Create a new Vec to hold the pointers to the strings
    let args_as_c_char = args.iter().map(|s| s.as_ptr()).collect::<Vec<_>>();

    // Call our function
    bar(args_as_c_char.as_ptr());
}

Я бы добавил к args_as_c_char нулевой указатель (как в случае с argv или environ), потому что подпись bar() не предполагает количество строк.

prog-fh 24.09.2023 20:51

Ваша точка зрения верна, я упустил аргумент размера.

user2722968 24.09.2023 22:28

Другие вопросы по теме