Skip to content

Add some convenience methods to go from CStr -> str #25416

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

Merged
merged 1 commit into from
May 23, 2015
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 72 additions & 0 deletions src/libstd/ffi/c_str.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

#![unstable(feature = "std_misc")]

use borrow::Cow;
use convert::{Into, From};
use cmp::{PartialEq, Eq, PartialOrd, Ord, Ordering};
use error::Error;
Expand All @@ -22,6 +23,7 @@ use ops::Deref;
use option::Option::{self, Some, None};
use result::Result::{self, Ok, Err};
use slice;
use str;
use string::String;
use vec::Vec;

Expand Down Expand Up @@ -114,6 +116,26 @@ pub struct CString {
/// work(&s);
/// }
/// ```
///
/// Converting a foreign C string into a Rust `String`
///
/// ```no_run
/// # #![feature(libc,cstr_to_str)]
/// extern crate libc;
/// use std::ffi::CStr;
///
/// extern { fn my_string() -> *const libc::c_char; }
///
/// fn my_string_safe() -> String {
/// unsafe {
/// CStr::from_ptr(my_string()).to_string_lossy().into_owned()
/// }
/// }
///
/// fn main() {
/// println!("string: {}", my_string_safe());
/// }
/// ```
#[derive(Hash)]
#[stable(feature = "rust1", since = "1.0.0")]
pub struct CStr {
Expand Down Expand Up @@ -328,6 +350,39 @@ impl CStr {
pub fn to_bytes_with_nul(&self) -> &[u8] {
unsafe { mem::transmute::<&[libc::c_char], &[u8]>(&self.inner) }
}

/// Yields a `&str` slice if the `CStr` contains valid UTF-8.
///
/// This function will calculate the length of this string and check for
/// UTF-8 validity, and then return the `&str` if it's valid.
///
/// > **Note**: This method is currently implemented to check for validity
/// > after a 0-cost cast, but it is planned to alter its definition in the
/// > future to perform the length calculation in addition to the UTF-8
/// > check whenever this method is called.
#[unstable(feature = "cstr_to_str", reason = "recently added")]
pub fn to_str(&self) -> Result<&str, str::Utf8Error> {
// NB: When CStr is changed to perform the length check in .to_bytes() instead of in
// from_ptr(), it may be worth considering if this should be rewritten to do the UTF-8
// check inline with the length calculation instead of doing it afterwards.
str::from_utf8(self.to_bytes())
}

/// Converts a `CStr` into a `Cow<str>`.
///
/// This function will calculate the length of this string (which normally
/// requires a linear amount of work to be done) and then return the
/// resulting slice as a `Cow<str>`, replacing any invalid UTF-8 sequences
/// with `U+FFFD REPLACEMENT CHARACTER`.
///
/// > **Note**: This method is currently implemented to check for validity
/// > after a 0-cost cast, but it is planned to alter its definition in the
/// > future to perform the length calculation in addition to the UTF-8
/// > check whenever this method is called.
#[unstable(feature = "cstr_to_str", reason = "recently added")]
pub fn to_string_lossy(&self) -> Cow<str> {
String::from_utf8_lossy(self.to_bytes())
}
}

#[stable(feature = "rust1", since = "1.0.0")]
Expand Down Expand Up @@ -356,6 +411,7 @@ mod tests {
use prelude::v1::*;
use super::*;
use libc;
use borrow::Cow::{Borrowed, Owned};

#[test]
fn c_to_rust() {
Expand Down Expand Up @@ -405,4 +461,20 @@ mod tests {
assert_eq!(s.to_bytes_with_nul(), b"12\0");
}
}

#[test]
fn to_str() {
let data = b"123\xE2\x80\xA6\0";
let ptr = data.as_ptr() as *const libc::c_char;
unsafe {
assert_eq!(CStr::from_ptr(ptr).to_str(), Ok("123…"));
assert_eq!(CStr::from_ptr(ptr).to_string_lossy(), Borrowed("123…"));
}
let data = b"123\xE2\0";
let ptr = data.as_ptr() as *const libc::c_char;
unsafe {
assert!(CStr::from_ptr(ptr).to_str().is_err());
assert_eq!(CStr::from_ptr(ptr).to_string_lossy(), Owned::<str>(format!("123\u{FFFD}")));
}
}
}