Hi,
consider this:
pub trait AsStr {
fn as_str(&self) -> &str;
fn to_utf16(&self) -> Vec<u16> {
self.as_str().to_utf16()
}
fn to_pcwstr(&self) -> PCWSTR {
PCWSTR(self.to_utf16().as_ptr())
}
}
In to_pcwstr
It seems that the Vec
created by to_utf16()
doesn't live long enough for the pointer created by as_ptr()
to be valid.
I'm encountering non systematic failure calling some Windows API and I've tracked down the cause to this.
Did I found a bug or (more likely), what am I missing?
The lifetime of pointers is not checked by the compiler.
The docs for as_ptr
mention this:
The caller must ensure that the vector outlives the pointer this function returns, or else it will end up pointing to garbage.
1 Like
In Rust, a str
always stores UTF-8 data. Therefore, whatever crate provides the str::to_utf16()
function must translate it into a new buffer. As a consequence, you cannot return a reference to the to_utf16()
output from to_pcwstr()
; you'll have to provide some kind of free function:
pub fn slice_as_pcwstr(slice: &[u16]) -> PCWSTR {
PCWSTR(slice.as_ptr())
}
pub fn example(s: impl AsStr) {
let buf: Vec<u8> = s.to_utf16();
let pcwstr: PCWSTR = slice_as_pcwstr(&buf);
some_api(pcwstr);
}
2 Likes
For things such as pointers which are not lifetime-tracked, I personally recommend using a scoped / callback pattern, so that programmers can easily view the area / scope of usability of the pointer:
fn with_pcwstr<R>(&self, scope: impl FnOnce(PCWSTR) -> R) -> R {
scope(PCWSTR(self.to_utf16().as_ptr()))
}
so as to:
let buf: Vec<u8> = s.to_utf16();
buf.with_pcwstr(|pcwstr| {
some_api(pcwstr);
}) // <- `pcwstr` valid until here; beyond this point it's a dangling pointer
1 Like