-
Notifications
You must be signed in to change notification settings - Fork 13k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Tracking issue for CString::{from_ptr, into_ptr} #27769
Comments
This feature is now entering its final comment period for stabilization. I will post a PR soon for renaming these function to |
Awesome! That makes sense to me. |
This rename is important because |
If you use |
We need to learn from these bugs. Premature dropping of |
@bluss an interesting question. In my mind, once you hit a raw pointer, all bets are off and you have to manage everything yourself. Do you have any thoughts on how to improve that with types? |
This commit renames the `CString::{into_ptr, from_ptr}` methods to `into_raw` and `from_raw` to mirror the corresponding methods on `Box` and the naming of "raw" for `from_raw_parts` on slices and vectors. cc rust-lang#27769
This commit renames the `CString::{into_ptr, from_ptr}` methods to `into_raw` and `from_raw` to mirror the corresponding methods on `Box` and the naming of "raw" for `from_raw_parts` on slices and vectors. cc #27769
You can still get some type-safety with raw pointers, though you don't get ownership tracking. I can see a potential API along these lines: #[repr(i8)] enum RustAllocChar {...}
impl CString {
fn into_raw(self) -> *const RustAllocChar;
unsafe fn from_raw(ptr: *const RustAllocChar) -> CString;
} which lets you write something like extern {
fn do_stuff(string: *mut RustAllocChar, free: fn(*mut RustAllocChar));
}
extern fn free(ptr: *mut RustAllocChar) {
drop(CString::from_raw(ptr));
}
fn main() {
let s = CString::new("Hello!").unwrap();
unsafe {do_stuff(s.into_raw(), free)};
} or for a library #[no_mangle] // char *mylib_do_stuff(void);
extern fn mylib_do_stuff() -> *mut RustOwnedChar {...}
#[no_mangle] // void mylib_free(char *s);
extern fn mylib_free(s: *mut RustOwnedChar) {...} So |
Also, while I was writing the above, I realized -- shouldn't Similarly, [EDIT: |
I disagree. There's no reason to assume that a
The problem is that we are dealing with C /FFI and these types are indistinguishable. I think that the additional cognitive overhead of having to remember that a It also sound like you are trying to use these methods inside of Rust code to call FFI code. That's definitely not the intended use case, as you don't want to have a one-way transfer of ownership due to the reallocation requirements.
That's a good reason to change them, and it was probably a mistake on my part originally. Honestly, the fluidity of |
Maybe I was unclear, but this depends specifically on annotating types with a Obviously this requires defining the FFI interfaces to use these types when appropriate, and not use them when inappropriate. I don't think that's too hard (we just need to pick a less stupid name than the ones I've come up with). To be fair, it is confusing that I'm distinguishing the type of the pointed-to thing, not the type of the pointer, when it is semantically a different sort of (raw) pointer to the same
I'm not clear on when you'd need to coerce it, in Rust code. The only purpose of a
Yeah, drop my first example with the callback because that's more confusing than it's worth. What I have in mind is implementing a C API contract that says something like, "This function returns a pointer. When you are done with it, free it by calling this other function," as you would do if you were implementing a C-ABI plugin spec, or writing a C-ABI library in Rust. Your example from #25777 does this. If I added both proper #[no_mangle]
pub extern fn reverse(s: *const libc::c_char) -> *mut RustAllocChar {
let s = unsafe { CStr::from_ptr(s) };
let s2 = s.to_str().unwrap();
let s3: String = s2.chars().rev().collect();
let s4 = CString::new(s3).unwrap();
s4.into_ptr()
}
#[no_mangle]
pub extern fn cleanup(s: *mut RustAllocChar) {
unsafe { CString::from_ptr(s) };
} #include <stdio.h>
extern char *reverse(const char *);
extern void cleanup(char *);
int main() {
char *s = reverse("Hello, world!");
printf("%s\n", s);
cleanup(s);
} which, at least on the Rust side, makes it clear what pointers are owned and which are borrowed. On the C side, the API docs or header files would probably have a comment on
Only when you're defining the FFI. Rust requires you to do an unsafe cast to turn Incidentally, the fact that C literals have type |
I agree with changing In general I feel the extra cognitive overhead of having an extra type just for this return value may not be worth the little bit of extra safety we get. For example there's still the case where |
... sigh, yes, you're right. OK, I withdraw that. So if the answer is that there's no practical way to increase the type-safety of these functions, that's fine. And |
Conventionally in C `*mut T` is a transfer of ownership where `*const T` is a loan, so `*mut T` is likely the more appropriate return type for these functions. Additionally, this more closely mirrors the APIs on `Box` for this sort of functionality. cc rust-lang#27769
Conventionally in C `*mut T` is a transfer of ownership where `*const T` is a loan, so `*mut T` is likely the more appropriate return type for these functions. Additionally, this more closely mirrors the APIs on `Box` for this sort of functionality. cc #27769
@gankro Some of the above discussion makes me think about your plans with |
The FCP is coming to a close and 1.4 is coming out soon, so this brings in the libs team decision for all library features this cycle. Stabilized APIs: * `<Box<str>>::into_string` * `Arc::downgrade` * `Arc::get_mut` * `Arc::make_mut` * `Arc::try_unwrap` * `Box::from_raw` * `Box::into_raw` * `CStr::to_str` * `CStr::to_string_lossy` * `CString::from_raw` * `CString::into_raw` * `IntoRawFd::into_raw_fd` * `IntoRawFd` * `IntoRawHandle::into_raw_handle` * `IntoRawHandle` * `IntoRawSocket::into_raw_socket` * `IntoRawSocket` * `Rc::downgrade` * `Rc::get_mut` * `Rc::make_mut` * `Rc::try_unwrap` * `Result::expect` * `String::into_boxed_slice` * `TcpSocket::read_timeout` * `TcpSocket::set_read_timeout` * `TcpSocket::set_write_timeout` * `TcpSocket::write_timeout` * `UdpSocket::read_timeout` * `UdpSocket::set_read_timeout` * `UdpSocket::set_write_timeout` * `UdpSocket::write_timeout` * `Vec::append` * `Vec::split_off` * `VecDeque::append` * `VecDeque::retain` * `VecDeque::split_off` * `rc::Weak::upgrade` * `rc::Weak` * `slice::Iter::as_slice` * `slice::IterMut::into_slice` * `str::CharIndices::as_str` * `str::Chars::as_str` * `str::split_at_mut` * `str::split_at` * `sync::Weak::upgrade` * `sync::Weak` * `thread::park_timeout` * `thread::sleep` Deprecated APIs * `BTreeMap::with_b` * `BTreeSet::with_b` * `Option::as_mut_slice` * `Option::as_slice` * `Result::as_mut_slice` * `Result::as_slice` * `f32::from_str_radix` * `f64::from_str_radix` Closes rust-lang#27277 Closes rust-lang#27718 Closes rust-lang#27736 Closes rust-lang#27764 Closes rust-lang#27765 Closes rust-lang#27766 Closes rust-lang#27767 Closes rust-lang#27768 Closes rust-lang#27769 Closes rust-lang#27771 Closes rust-lang#27773 Closes rust-lang#27775 Closes rust-lang#27776 Closes rust-lang#27785 Closes rust-lang#27792 Closes rust-lang#27795 Closes rust-lang#27797
The FCP is coming to a close and 1.4 is coming out soon, so this brings in the libs team decision for all library features this cycle. Stabilized APIs: * `<Box<str>>::into_string` * `Arc::downgrade` * `Arc::get_mut` * `Arc::make_mut` * `Arc::try_unwrap` * `Box::from_raw` * `Box::into_raw` * `CStr::to_str` * `CStr::to_string_lossy` * `CString::from_raw` * `CString::into_raw` * `IntoRawFd::into_raw_fd` * `IntoRawFd` * `IntoRawHandle::into_raw_handle` * `IntoRawHandle` * `IntoRawSocket::into_raw_socket` * `IntoRawSocket` * `Rc::downgrade` * `Rc::get_mut` * `Rc::make_mut` * `Rc::try_unwrap` * `Result::expect` * `String::into_boxed_slice` * `TcpSocket::read_timeout` * `TcpSocket::set_read_timeout` * `TcpSocket::set_write_timeout` * `TcpSocket::write_timeout` * `UdpSocket::read_timeout` * `UdpSocket::set_read_timeout` * `UdpSocket::set_write_timeout` * `UdpSocket::write_timeout` * `Vec::append` * `Vec::split_off` * `VecDeque::append` * `VecDeque::retain` * `VecDeque::split_off` * `rc::Weak::upgrade` * `rc::Weak` * `slice::Iter::as_slice` * `slice::IterMut::into_slice` * `str::CharIndices::as_str` * `str::Chars::as_str` * `str::split_at_mut` * `str::split_at` * `sync::Weak::upgrade` * `sync::Weak` * `thread::park_timeout` * `thread::sleep` Deprecated APIs * `BTreeMap::with_b` * `BTreeSet::with_b` * `Option::as_mut_slice` * `Option::as_slice` * `Result::as_mut_slice` * `Result::as_slice` * `f32::from_str_radix` * `f64::from_str_radix` Closes #27277 Closes #27718 Closes #27736 Closes #27764 Closes #27765 Closes #27766 Closes #27767 Closes #27768 Closes #27769 Closes #27771 Closes #27773 Closes #27775 Closes #27776 Closes #27785 Closes #27792 Closes #27795 Closes #27797
This is a tracking issue for the unstable
cstr_memory
feature in the standard library. These methods are likely ready to go as-is modulo naming. Their names should be consistent withBox::{from_raw, into_raw}
(currently inconsistent).cc #27768
The text was updated successfully, but these errors were encountered: