如何从 &str 转换为 *const i8 *without* libstd 和 libcore?

How to convert from &str to *const i8 *without* libstd and libcore?

我有一个场景,大约 15 年前我们有一个用 C 编写的现有(旧)操作系统。现在,我们正在考虑扩展这个系统,能够用 Rust 编写用户 space 程序。

自然地,因为这是最近才开始的,我们还没有费心将所有 libstd 移植到我们自己的 OS。因此我们使用 #![feature(no_std)].

现在,我正在寻找应该相当简单的东西:将 Rust 字符串转换为 C-null 终止的字符串。应该很简单,但是因为我对 Rust 缺乏经验,所以我(还)没有弄明白。

为了这种体验,施加某些限制就足够了(例如,最大 1024 字节长的字符串;其他任何内容都会被截断)。 (我们确实有内存分配,但我还没有费心尝试处理 Rust 的内存分配)

这是我迄今为止的微弱尝试:

pub struct CString {
    buffer: [i8; 1024]
}

impl CString {
    pub fn new(s: &str) -> CString {
        CString {
            buffer: CString::to_c_string(s)
        }
    }

    fn to_c_string(s: &str) -> [i8; 1024] {
        let buffer: [i8; 1024];
        let mut i = 0;

        // TODO: ignore the risk for buffer overruns for now. :)
        // TODO: likewise with UTF8; assume that we are ASCII-only.
        for c in s.chars() {
            buffer[i] = c as i8;
            i = i + 1;
        }

        buffer[s.len()] = '[=10=]' as i8;
        buffer;
    }

    pub fn as_ptr(&self) -> *const i8 {    
        // TODO: Implement. The line below doesn't even compile.
        self.buffer as *const i8
    }
}

这里的核心问题是as_ptr中的类型转换。你如何在 Rust 中做到这一点?另外,除了明显的问题之外,这段代码还有其他问题吗? (损坏的 UTF8 非 ASCII 字符处理,如果字符串超过 1024 个字符则完全愚蠢...:)

非常感谢!这必须是相当明显的东西...


更新:根据 Will Fischer 的回答(谢谢!),我将 as_ptr 方法更改为如下所示:

pub fn as_ptr(&self) -> *const i8 {
    &self.buffer as *const i8
}

代码现在可以编译,但没有 link:

virtio_net_pci.0.rs:(.text._ZN6system8c_string7CString3new20hbfc6c6db748de66bpaaE+0x31): undefined reference to `memset'
virtio_net_pci.0.rs:(.text._ZN6system8c_string7CString3new20hbfc6c6db748de66bpaaE+0x14f): undefined reference to `memcpy'
virtio_net_pci.0.rs:(.text._ZN6system8c_string7CString3new20hbfc6c6db748de66bpaaE+0x174): undefined reference to `panicking::panic_bounds_check::h0b7be17a72a754b5P6E'
virtio_net_pci.0.rs:(.text._ZN6system8c_string7CString3new20hbfc6c6db748de66bpaaE+0x18c): undefined reference to `panicking::panic_bounds_check::h0b7be17a72a754b5P6E'
collect2: error: ld returned 1 exit status

memsetmemcpy 的东西很容易修复。我假设的边界检查是在 libcore 中实现的——有什么方法可以在不 link 到 libcore 的情况下使它工作? (无论如何这可能是一件合理的事情......)

不是转换缓冲区,而是转换对缓冲区的引用。 Example

fn main() {
    let buffer: [i8; 1024] = [42; 1024];
    let ptr: *const i8 = &buffer as *const i8;
    unsafe {
        println!("{}", *ptr.offset(30));
    }
}

您是否尝试过让您的代码与 libcore 一起工作?它是标准库的一个子集,涵盖了不需要 OS 支持的所有内容。你可以通过它获得字符串操作函数。