2016-08-15 163 views
5

给定std::path::Path,将其转换为以空字符结尾的最直接方法是什么std::os::raw::c_char? (用于传递给具有路径的C函数)。将路径转换为* c_char最直接的方法是什么?

use std::ffi::CString; 
use std::os::raw::c_char; 
use std::os::raw::c_void; 

extern "C" { 
    some_c_function(path: *const c_char); 
} 

fn example_c_wrapper(path: std::path::Path) { 
    let path_str_c = CString::new(path.as_os_str().to_str().unwrap()).unwrap(); 

    some_c_function(path_str_c.as_ptr()); 
} 

有没有办法避免这么多中间步骤?

Path -> OsStr -> &str -> CString -> as_ptr() 
+0

假设'Path'可以转换为C字符串并不准确。平台可以并且确实使用不同的编码;这就是为什么这些抽象首先存在的原因。如果您限制为类UNIX系统,则有['OsStrExt'](https://doc.rust-lang.org/std/os/unix/ffi/trait.OsStrExt.html)。 – Shepmaster

+0

另外请注意,您也正在转换为'String',它必须是UTF-8,尽管C字符串不需要。 – Shepmaster

回答

4

这并不像看起来那么容易。有一条你没有提供的信息:预期路径的C函数是什么编码?

在Linux上,路径是“正好”字节数组(0无效),应用程序通常不会尝试解码它们。 (但是,他们可能不得不用特定的编码来解码它们,例如将它们显示给用户,在这种情况下,他们通常会根据当前的区域设置对它们进行解码,这通常会使用UTF-8编码。)

在Windows上,它更复杂,因为API函数的变体使用“ANSI”代码页和使用“Unicode”(UTF-16)的变体。此外,Windows不支持将UTF-8设置为“ANSI”代码页。这意味着,除非库特别期待UTF-8并将路径转换为本地编码本身,否则将它传递给UTF-8编码路径肯定是错误的(尽管可能似乎仅适用于仅包含ASCII字符的字符串)。

(我不知道其他平台,但它足以乱了。)

生锈,Path仅仅是OsStr的包装。 OsStr使用平台相关的表示法,当字符串确实是有效的UTF-8时,它恰好与UTF-8兼容,但非UTF-8字符串使用未指定的编码(在Windows上,它实际上使用的是WTF-8,但这不是合约;在Linux上,它只是字节数组)。

在你传递一个C函数的路径之前,你必须确定它需要的字符串是什么编码,如果它不符合Rust的编码,你必须在将它转换为CString 。 Rust不允许您以独立于平台的方式将PathOsStr转换为除str以外的其他任何内容。在基于Unix的目标上,OsStrExt特征可用,并提供对OsStr作为一个字节片段的访问。

Rust用于在OsStr上提供to_cstring方法,但它从未稳定过,并且在Rust 1.6.0中已弃用,因为它意识到该行为对Windows不适用(它返回了UTF-8编码路径,但Windows API不支持!)。

+2

区域设置是对Linux的系统猜测,但它并不真正与路径编码相关。路径可以是除0之外的任意字节。 – bluss

相关问题