2016-08-15 49 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

相關問題