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

Converting output of libc::getcwd into a string

我想打印出 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> 并在 getcwd 调用中将其转换为 c_char 缓冲区:

    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 returns NULL(libc::getcwd(...).is_null() 为真),通过外部 crate errno 读取最后一个错误(为什么这是一个单独的 crate to libc ?) 显示 getcwd 失败并显示 "Invalid argument"。问题的根源似乎是 buf.len() returns 0.

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


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

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

容量是有多少内存可用,大小是使用了多少。将向量视为存储数据的 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());
}

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

当一个块结束时,其中的任何变量都将被删除。在这种情况下,unsafe 块创建 buf,获取指向它的指针,使用指针创建 CStr,然后 释放 Vec,使指针无效。然后 returns 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 块的部分原因