Я пытаюсь сопоставить аргументы программы, которые задаются как 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`

Тип 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()не предполагает количество строк.