gpt4 book ai didi

rust - 将 libc::getcwd 的输出转换为字符串

转载 作者:行者123 更新时间:2023-11-29 07:59:49 24 4
gpt4 key购买 nike

我想打印出 libc::getcwd 的结果.我的问题是创建 getcwd需要 i8 ( c_char ) 缓冲区,而 String::from_utf8需要 u8缓冲。我开始于:

static BUF_BYTES: usize = 4096;

fn main() {
unsafe {
let mut buf: Vec<i8> = Vec::with_capacity(BUF_BYTES as usize);
libc::getcwd(buf.as_mut_ptr(), buf.len());
let s = String::from_utf8(buf).expect("Found invalid UTF-8");
println!("result: {}", s);
}
}

产生错误的地方:

14:32 error: mismatched types:
expected `std::vec::Vec<u8>`,
found `std::vec::Vec<i8>` [E0308]

感谢评论,我更改了 buf进入 Vec<u8>并将其转换为 c_char getcwd 中的缓冲区调用:

    let mut buf: Vec<u8> = Vec::with_capacity(BUF_BYTES as usize);
libc::getcwd(buf.as_mut_ptr() as *mut c_char, buf.len());

这可以编译,但是现在,当打印字符串时它是空的(长度:0)

我发现 getcwd返回 NULL(libc::getcwd(...).is_null() 为真),通过外部 crate 读取最后一个错误 errno (为什么这是一个独立于 libc 的箱子?)揭示了 getcwd失败并显示“无效参数”。问题的根源似乎是 buf.len()返回 0。

最佳答案

大多数情况下,您应该只使用env::current_dir .这会为您正确处理所有特定于平台的问题,例如评论中提到的“其他”编码。


C 字符串有点糟糕。 getcwd 填充一定长度的缓冲区,但不告诉您它在哪里结束;您必须手动找到终止 NUL 字节。

extern crate libc;

static BUF_BYTES: usize = 4096;

fn main() {
let buf = unsafe {
let mut buf = Vec::with_capacity(BUF_BYTES);
let res = libc::getcwd(buf.as_mut_ptr() as *mut i8, buf.capacity());
if res.is_null() {
panic!("Not long enough");
}
let mut len = 0;
while *buf.as_mut_ptr().offset(len as isize) != 0 { len += 1 }
buf.set_len(len);
buf
};

let s = String::from_utf8(buf).expect("Found invalid UTF-8");
println!("result: {}", s);
}

seems that buf.len() returns 0

是的,长度为零,因为没有人告诉 vector 添加了数据。矢量由三部分组成 - 指向数据的指针、长度和容量

容量是有多少内存可用,大小是使用了多少。将向量视为存储数据的 blob 时,您需要使用容量。然后,您需要通知向量使用了多少字节,以便 String::from_utf8 知道结束位置。

您会注意到,我将 unsafe 的范围更改为仅包括真正不安全的方面以及使该代码真正安全的代码。


事实上,您可以只复制 the implementation of env::current_dir for Unix-like systems .它可以更好地处理失败情况并使用正确的类型(路径不是 字符串)。当然,调用 env::current_dir 更简单。 ^_^


fyi: I ended up with this

extern crate libc;

use std::ffi::CStr;
use std::io;
use std::str;

static BUF_BYTES: usize = 4096;

fn main() {
let buf = unsafe {
let mut buf = Vec::with_capacity(BUF_BYTES);
let ptr = buf.as_mut_ptr() as *mut libc::c_char;
if libc::getcwd(ptr, buf.capacity()).is_null() {
panic!(io::Error::last_os_error());
}
CStr::from_ptr(ptr).to_bytes()
};
println!("result: {}", str::from_utf8(buf).unwrap());
}

这是不安全,会导致崩溃(在最好的情况下)或静默内存损坏或更糟。

当一个 block 结束时,其中的任何变量都将被删除。在这种情况下,unsafe block 创建 buf,获取一个指向它的指针,用该指针创建一个 CStr,然后 释放Vec,使指针无效。然后它返回包含来自该 block 的无效引用的 CStr

像这样更好:

extern crate libc;

use std::ffi::{CStr, CString};
use std::io;
use std::str;

static BUF_BYTES: usize = 4096;

fn main() {
let buf = unsafe {
// Allocate some space to store the result
let mut buf = Vec::with_capacity(BUF_BYTES);

// Call the function, panicking if it fails
let ptr = buf.as_mut_ptr() as *mut libc::c_char;
if libc::getcwd(ptr, buf.capacity()).is_null() {
panic!(io::Error::last_os_error());
}

// Find the first NUL and inform the vector of that
let s = CStr::from_ptr(ptr);
buf.set_len(s.to_bytes().len());

// Transfer ownership of the Vec to a CString, ensuring there are no interior NULs
CString::new(buf)
};

let s = buf.expect("Not a C string").into_string().expect("Not UTF-8");
println!("result: {}", s);
}

I wonder why this has actually worked

可能是因为在您尝试访问内存之前没有任何更改。在高度多线程的环境中,我可能会发现更多问题。

why is it possible to have two mutable references to the vector? First as mut buf and then as ptr = buf.as_mut_ptr(). The ownership has not moved, has it? Otherwise, why is it possible to call buf.capacity()

您实际上没有两个引用buf 拥有该值,然后您将获得一个可变指针。指针没有编译器保护,这是需要 unsafe block 的部分原因

关于rust - 将 libc::getcwd 的输出转换为字符串,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38196386/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com