about summary refs log tree commit diff
path: root/src/libstd
diff options
context:
space:
mode:
authorAlex Crichton <alex@alexcrichton.com>2014-11-25 13:28:35 -0800
committerAlex Crichton <alex@alexcrichton.com>2015-01-05 08:00:13 -0800
commitec7a50d20dff416d9fec837a6492dfe244f5f3ab (patch)
tree559c2e05abb4f51d6309a1af3f4e1953a6627e3d /src/libstd
parent1f732ef53d54ccfc3e7728390ffbcea8a696ecee (diff)
downloadrust-ec7a50d20dff416d9fec837a6492dfe244f5f3ab.tar.gz
rust-ec7a50d20dff416d9fec837a6492dfe244f5f3ab.zip
std: Redesign c_str and c_vec
This commit is an implementation of [RFC 494][rfc] which removes the entire
`std::c_vec` module and redesigns the `std::c_str` module as `std::ffi`.

[rfc]: https://github.com/rust-lang/rfcs/blob/master/text/0494-c_str-and-c_vec-stability.md

The interface of the new `CString` is outlined in the linked RFC, the primary
changes being:

* The `ToCStr` trait is gone, meaning the `with_c_str` and `to_c_str` methods
  are now gone. These two methods are replaced with a `CString::from_slice`
  method.
* The `CString` type is now just a wrapper around `Vec<u8>` with a static
  guarantee that there is a trailing nul byte with no internal nul bytes. This
  means that `CString` now implements `Deref<Target = [c_char]>`, which is where
  it gains most of its methods from. A few helper methods are added to acquire a
  slice of `u8` instead of `c_char`, as well as including a slice with the
  trailing nul byte if necessary.
* All usage of non-owned `CString` values is now done via two functions inside
  of `std::ffi`, called `c_str_to_bytes` and `c_str_to_bytes_with_nul`. These
  functions are now the one method used to convert a `*const c_char` to a Rust
  slice of `u8`.

Many more details, including newly deprecated methods, can be found linked in
the RFC. This is a:

[breaking-change]
Closes #20444
Diffstat (limited to 'src/libstd')
-rw-r--r--src/libstd/c_str.rs857
-rw-r--r--src/libstd/c_vec.rs232
-rw-r--r--src/libstd/dynamic_lib.rs41
-rw-r--r--src/libstd/ffi/c_str.rs218
-rw-r--r--src/libstd/ffi/mod.rs20
-rw-r--r--src/libstd/io/net/pipe.rs21
-rw-r--r--src/libstd/io/process.rs81
-rw-r--r--src/libstd/lib.rs6
-rw-r--r--src/libstd/os.rs41
-rw-r--r--src/libstd/path/mod.rs22
-rw-r--r--src/libstd/path/posix.rs31
-rw-r--r--src/libstd/path/windows.rs26
-rw-r--r--src/libstd/rt/args.rs11
-rw-r--r--src/libstd/rt/backtrace.rs2
-rw-r--r--src/libstd/rt/unwind.rs2
-rw-r--r--src/libstd/rt/util.rs2
-rw-r--r--src/libstd/sys/common/mod.rs2
-rw-r--r--src/libstd/sys/common/net.rs12
-rw-r--r--src/libstd/sys/unix/backtrace.rs23
-rw-r--r--src/libstd/sys/unix/fs.rs48
-rw-r--r--src/libstd/sys/unix/mod.rs13
-rw-r--r--src/libstd/sys/unix/os.rs31
-rw-r--r--src/libstd/sys/unix/pipe.rs4
-rw-r--r--src/libstd/sys/unix/process.rs6
-rw-r--r--src/libstd/sys/unix/timer.rs2
-rw-r--r--src/libstd/sys/windows/backtrace.rs18
-rw-r--r--src/libstd/sys/windows/c.rs17
-rw-r--r--src/libstd/sys/windows/fs.rs1
-rw-r--r--src/libstd/sys/windows/pipe.rs21
-rw-r--r--src/libstd/sys/windows/process.rs31
30 files changed, 463 insertions, 1379 deletions
diff --git a/src/libstd/c_str.rs b/src/libstd/c_str.rs
deleted file mode 100644
index 9c96a9cac78..00000000000
--- a/src/libstd/c_str.rs
+++ /dev/null
@@ -1,857 +0,0 @@
-// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-//! C-string manipulation and management
-//!
-//! This modules provides the basic methods for creating and manipulating
-//! null-terminated strings for use with FFI calls (back to C). Most C APIs require
-//! that the string being passed to them is null-terminated, and by default rust's
-//! string types are *not* null terminated.
-//!
-//! The other problem with translating Rust strings to C strings is that Rust
-//! strings can validly contain a null-byte in the middle of the string (0 is a
-//! valid Unicode codepoint). This means that not all Rust strings can actually be
-//! translated to C strings.
-//!
-//! # Creation of a C string
-//!
-//! A C string is managed through the `CString` type defined in this module. It
-//! "owns" the internal buffer of characters and will automatically deallocate the
-//! buffer when the string is dropped. The `ToCStr` trait is implemented for `&str`
-//! and `&[u8]`, but the conversions can fail due to some of the limitations
-//! explained above.
-//!
-//! This also means that currently whenever a C string is created, an allocation
-//! must be performed to place the data elsewhere (the lifetime of the C string is
-//! not tied to the lifetime of the original string/data buffer). If C strings are
-//! heavily used in applications, then caching may be advisable to prevent
-//! unnecessary amounts of allocations.
-//!
-//! Be carefull to remember that the memory is managed by C allocator API and not
-//! by Rust allocator API.
-//! That means that the CString pointers should be freed with C allocator API
-//! if you intend to do that on your own, as the behaviour if you free them with
-//! Rust's allocator API is not well defined
-//!
-//! An example of creating and using a C string would be:
-//!
-//! ```rust
-//! extern crate libc;
-//!
-//! use std::c_str::ToCStr;
-//!
-//! extern {
-//!     fn puts(s: *const libc::c_char);
-//! }
-//!
-//! fn main() {
-//!     let my_string = "Hello, world!";
-//!
-//!     // Allocate the C string with an explicit local that owns the string. The
-//!     // `c_buffer` pointer will be deallocated when `my_c_string` goes out of scope.
-//!     let my_c_string = my_string.to_c_str();
-//!     unsafe {
-//!         puts(my_c_string.as_ptr());
-//!     }
-//!
-//!     // Don't save/return the pointer to the C string, the `c_buffer` will be
-//!     // deallocated when this block returns!
-//!     my_string.with_c_str(|c_buffer| {
-//!         unsafe { puts(c_buffer); }
-//!     });
-//! }
-//! ```
-
-use core::prelude::*;
-use libc;
-
-use cmp::Ordering;
-use fmt;
-use hash;
-use mem;
-use ptr;
-use slice::{self, IntSliceExt};
-use str;
-use string::String;
-use core::kinds::marker;
-
-/// The representation of a C String.
-///
-/// This structure wraps a `*libc::c_char`, and will automatically free the
-/// memory it is pointing to when it goes out of scope.
-#[allow(missing_copy_implementations)]
-pub struct CString {
-    buf: *const libc::c_char,
-    owns_buffer_: bool,
-}
-
-unsafe impl Send for CString { }
-unsafe impl Sync for CString { }
-
-impl Clone for CString {
-    /// Clone this CString into a new, uniquely owned CString. For safety
-    /// reasons, this is always a deep clone with the memory allocated
-    /// with C's allocator API, rather than the usual shallow clone.
-    fn clone(&self) -> CString {
-        let len = self.len() + 1;
-        let buf = unsafe { libc::malloc(len as libc::size_t) } as *mut libc::c_char;
-        if buf.is_null() { ::alloc::oom() }
-        unsafe { ptr::copy_nonoverlapping_memory(buf, self.buf, len); }
-        CString { buf: buf as *const libc::c_char, owns_buffer_: true }
-    }
-}
-
-impl PartialEq for CString {
-    fn eq(&self, other: &CString) -> bool {
-        // Check if the two strings share the same buffer
-        if self.buf as uint == other.buf as uint {
-            true
-        } else {
-            unsafe {
-                libc::strcmp(self.buf, other.buf) == 0
-            }
-        }
-    }
-}
-
-impl PartialOrd for CString {
-    #[inline]
-    fn partial_cmp(&self, other: &CString) -> Option<Ordering> {
-        self.as_bytes().partial_cmp(other.as_bytes())
-    }
-}
-
-impl Eq for CString {}
-
-impl<S: hash::Writer> hash::Hash<S> for CString {
-    #[inline]
-    fn hash(&self, state: &mut S) {
-        self.as_bytes().hash(state)
-    }
-}
-
-impl CString {
-    /// Create a C String from a pointer, with memory managed by C's allocator
-    /// API, so avoid calling it with a pointer to memory managed by Rust's
-    /// allocator API, as the behaviour would not be well defined.
-    ///
-    ///# Panics
-    ///
-    /// Panics if `buf` is null
-    pub unsafe fn new(buf: *const libc::c_char, owns_buffer: bool) -> CString {
-        assert!(!buf.is_null());
-        CString { buf: buf, owns_buffer_: owns_buffer }
-    }
-
-    /// Return a pointer to the NUL-terminated string data.
-    ///
-    /// `.as_ptr` returns an internal pointer into the `CString`, and
-    /// may be invalidated when the `CString` falls out of scope (the
-    /// destructor will run, freeing the allocation if there is
-    /// one).
-    ///
-    /// ```rust
-    /// use std::c_str::ToCStr;
-    ///
-    /// let foo = "some string";
-    ///
-    /// // right
-    /// let x = foo.to_c_str();
-    /// let p = x.as_ptr();
-    ///
-    /// // wrong (the CString will be freed, invalidating `p`)
-    /// let p = foo.to_c_str().as_ptr();
-    /// ```
-    ///
-    /// # Example
-    ///
-    /// ```rust
-    /// extern crate libc;
-    ///
-    /// use std::c_str::ToCStr;
-    ///
-    /// fn main() {
-    ///     let c_str = "foo bar".to_c_str();
-    ///     unsafe {
-    ///         libc::puts(c_str.as_ptr());
-    ///     }
-    /// }
-    /// ```
-    pub fn as_ptr(&self) -> *const libc::c_char {
-        self.buf
-    }
-
-    /// Return a mutable pointer to the NUL-terminated string data.
-    ///
-    /// `.as_mut_ptr` returns an internal pointer into the `CString`, and
-    /// may be invalidated when the `CString` falls out of scope (the
-    /// destructor will run, freeing the allocation if there is
-    /// one).
-    ///
-    /// ```rust
-    /// use std::c_str::ToCStr;
-    ///
-    /// let foo = "some string";
-    ///
-    /// // right
-    /// let mut x = foo.to_c_str();
-    /// let p = x.as_mut_ptr();
-    ///
-    /// // wrong (the CString will be freed, invalidating `p`)
-    /// let p = foo.to_c_str().as_mut_ptr();
-    /// ```
-    pub fn as_mut_ptr(&mut self) -> *mut libc::c_char {
-        self.buf as *mut _
-    }
-
-    /// Returns whether or not the `CString` owns the buffer.
-    pub fn owns_buffer(&self) -> bool {
-        self.owns_buffer_
-    }
-
-    /// Converts the CString into a `&[u8]` without copying.
-    /// Includes the terminating NUL byte.
-    #[inline]
-    pub fn as_bytes<'a>(&'a self) -> &'a [u8] {
-        unsafe {
-            slice::from_raw_buf(&self.buf, self.len() + 1).as_unsigned()
-        }
-    }
-
-    /// Converts the CString into a `&[u8]` without copying.
-    /// Does not include the terminating NUL byte.
-    #[inline]
-    pub fn as_bytes_no_nul<'a>(&'a self) -> &'a [u8] {
-        unsafe {
-            slice::from_raw_buf(&self.buf, self.len()).as_unsigned()
-        }
-    }
-
-    /// Converts the CString into a `&str` without copying.
-    /// Returns None if the CString is not UTF-8.
-    #[inline]
-    pub fn as_str<'a>(&'a self) -> Option<&'a str> {
-        let buf = self.as_bytes_no_nul();
-        str::from_utf8(buf).ok()
-    }
-
-    /// Return a CString iterator.
-    pub fn iter<'a>(&'a self) -> CChars<'a> {
-        CChars {
-            ptr: self.buf,
-            marker: marker::ContravariantLifetime,
-        }
-    }
-
-    /// Unwraps the wrapped `*libc::c_char` from the `CString` wrapper.
-    ///
-    /// Any ownership of the buffer by the `CString` wrapper is
-    /// forgotten, meaning that the backing allocation of this
-    /// `CString` is not automatically freed if it owns the
-    /// allocation. In this case, a user of `.unwrap()` should ensure
-    /// the allocation is freed, to avoid leaking memory. You should
-    /// use libc's memory allocator in this case.
-    ///
-    /// Prefer `.as_ptr()` when just retrieving a pointer to the
-    /// string data, as that does not relinquish ownership.
-    pub unsafe fn into_inner(mut self) -> *const libc::c_char {
-        self.owns_buffer_ = false;
-        self.buf
-    }
-
-    /// Return the number of bytes in the CString (not including the NUL
-    /// terminator).
-    #[inline]
-    pub fn len(&self) -> uint {
-        unsafe { libc::strlen(self.buf) as uint }
-    }
-
-    /// Returns if there are no bytes in this string
-    #[inline]
-    pub fn is_empty(&self) -> bool { self.len() == 0 }
-}
-
-impl Drop for CString {
-    fn drop(&mut self) {
-        if self.owns_buffer_ {
-            unsafe {
-                libc::free(self.buf as *mut libc::c_void)
-            }
-        }
-    }
-}
-
-impl fmt::Show for CString {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        String::from_utf8_lossy(self.as_bytes_no_nul()).fmt(f)
-    }
-}
-
-/// A generic trait for converting a value to a CString.
-pub trait ToCStr for Sized? {
-    /// Copy the receiver into a CString.
-    ///
-    /// # Panics
-    ///
-    /// Panics the task if the receiver has an interior null.
-    fn to_c_str(&self) -> CString;
-
-    /// Unsafe variant of `to_c_str()` that doesn't check for nulls.
-    unsafe fn to_c_str_unchecked(&self) -> CString;
-
-    /// Work with a temporary CString constructed from the receiver.
-    /// The provided `*libc::c_char` will be freed immediately upon return.
-    ///
-    /// # Example
-    ///
-    /// ```rust
-    /// extern crate libc;
-    ///
-    /// use std::c_str::ToCStr;
-    ///
-    /// fn main() {
-    ///     let s = "PATH".with_c_str(|path| unsafe {
-    ///         libc::getenv(path)
-    ///     });
-    /// }
-    /// ```
-    ///
-    /// # Panics
-    ///
-    /// Panics the task if the receiver has an interior null.
-    #[inline]
-    fn with_c_str<T, F>(&self, f: F) -> T where
-        F: FnOnce(*const libc::c_char) -> T,
-    {
-        let c_str = self.to_c_str();
-        f(c_str.as_ptr())
-    }
-
-    /// Unsafe variant of `with_c_str()` that doesn't check for nulls.
-    #[inline]
-    unsafe fn with_c_str_unchecked<T, F>(&self, f: F) -> T where
-        F: FnOnce(*const libc::c_char) -> T,
-    {
-        let c_str = self.to_c_str_unchecked();
-        f(c_str.as_ptr())
-    }
-}
-
-impl ToCStr for str {
-    #[inline]
-    fn to_c_str(&self) -> CString {
-        self.as_bytes().to_c_str()
-    }
-
-    #[inline]
-    unsafe fn to_c_str_unchecked(&self) -> CString {
-        self.as_bytes().to_c_str_unchecked()
-    }
-
-    #[inline]
-    fn with_c_str<T, F>(&self, f: F) -> T where
-        F: FnOnce(*const libc::c_char) -> T,
-    {
-        self.as_bytes().with_c_str(f)
-    }
-
-    #[inline]
-    unsafe fn with_c_str_unchecked<T, F>(&self, f: F) -> T where
-        F: FnOnce(*const libc::c_char) -> T,
-    {
-        self.as_bytes().with_c_str_unchecked(f)
-    }
-}
-
-impl ToCStr for String {
-    #[inline]
-    fn to_c_str(&self) -> CString {
-        self.as_bytes().to_c_str()
-    }
-
-    #[inline]
-    unsafe fn to_c_str_unchecked(&self) -> CString {
-        self.as_bytes().to_c_str_unchecked()
-    }
-
-    #[inline]
-    fn with_c_str<T, F>(&self, f: F) -> T where
-        F: FnOnce(*const libc::c_char) -> T,
-    {
-        self.as_bytes().with_c_str(f)
-    }
-
-    #[inline]
-    unsafe fn with_c_str_unchecked<T, F>(&self, f: F) -> T where
-        F: FnOnce(*const libc::c_char) -> T,
-    {
-        self.as_bytes().with_c_str_unchecked(f)
-    }
-}
-
-// The length of the stack allocated buffer for `vec.with_c_str()`
-const BUF_LEN: uint = 128;
-
-impl ToCStr for [u8] {
-    fn to_c_str(&self) -> CString {
-        let mut cs = unsafe { self.to_c_str_unchecked() };
-        check_for_null(self, cs.as_mut_ptr());
-        cs
-    }
-
-    unsafe fn to_c_str_unchecked(&self) -> CString {
-        let self_len = self.len();
-        let buf = libc::malloc(self_len as libc::size_t + 1) as *mut u8;
-        if buf.is_null() { ::alloc::oom() }
-
-        ptr::copy_memory(buf, self.as_ptr(), self_len);
-        *buf.offset(self_len as int) = 0;
-
-        CString::new(buf as *const libc::c_char, true)
-    }
-
-    fn with_c_str<T, F>(&self, f: F) -> T where
-        F: FnOnce(*const libc::c_char) -> T,
-    {
-        unsafe { with_c_str(self, true, f) }
-    }
-
-    unsafe fn with_c_str_unchecked<T, F>(&self, f: F) -> T where
-        F: FnOnce(*const libc::c_char) -> T,
-    {
-        with_c_str(self, false, f)
-    }
-}
-
-impl<'a, Sized? T: ToCStr> ToCStr for &'a T {
-    #[inline]
-    fn to_c_str(&self) -> CString {
-        (**self).to_c_str()
-    }
-
-    #[inline]
-    unsafe fn to_c_str_unchecked(&self) -> CString {
-        (**self).to_c_str_unchecked()
-    }
-
-    #[inline]
-    fn with_c_str<T, F>(&self, f: F) -> T where
-        F: FnOnce(*const libc::c_char) -> T,
-    {
-        (**self).with_c_str(f)
-    }
-
-    #[inline]
-    unsafe fn with_c_str_unchecked<T, F>(&self, f: F) -> T where
-        F: FnOnce(*const libc::c_char) -> T,
-    {
-        (**self).with_c_str_unchecked(f)
-    }
-}
-
-// Unsafe function that handles possibly copying the &[u8] into a stack array.
-unsafe fn with_c_str<T, F>(v: &[u8], checked: bool, f: F) -> T where
-    F: FnOnce(*const libc::c_char) -> T,
-{
-    let c_str = if v.len() < BUF_LEN {
-        let mut buf: [u8; BUF_LEN] = mem::uninitialized();
-        slice::bytes::copy_memory(&mut buf, v);
-        buf[v.len()] = 0;
-
-        let buf = buf.as_mut_ptr();
-        if checked {
-            check_for_null(v, buf as *mut libc::c_char);
-        }
-
-        return f(buf as *const libc::c_char)
-    } else if checked {
-        v.to_c_str()
-    } else {
-        v.to_c_str_unchecked()
-    };
-
-    f(c_str.as_ptr())
-}
-
-#[inline]
-fn check_for_null(v: &[u8], buf: *mut libc::c_char) {
-    for i in range(0, v.len()) {
-        unsafe {
-            let p = buf.offset(i as int);
-            assert!(*p != 0);
-        }
-    }
-}
-
-/// External iterator for a CString's bytes.
-///
-/// Use with the `std::iter` module.
-#[allow(raw_pointer_deriving)]
-#[derive(Clone)]
-pub struct CChars<'a> {
-    ptr: *const libc::c_char,
-    marker: marker::ContravariantLifetime<'a>,
-}
-
-impl<'a> Iterator for CChars<'a> {
-    type Item = libc::c_char;
-
-    fn next(&mut self) -> Option<libc::c_char> {
-        let ch = unsafe { *self.ptr };
-        if ch == 0 {
-            None
-        } else {
-            self.ptr = unsafe { self.ptr.offset(1) };
-            Some(ch)
-        }
-    }
-}
-
-/// Parses a C "multistring", eg windows env values or
-/// the req->ptr result in a uv_fs_readdir() call.
-///
-/// Optionally, a `count` can be passed in, limiting the
-/// parsing to only being done `count`-times.
-///
-/// The specified closure is invoked with each string that
-/// is found, and the number of strings found is returned.
-pub unsafe fn from_c_multistring<F>(buf: *const libc::c_char,
-                                    count: Option<uint>,
-                                    mut f: F)
-                                    -> uint where
-    F: FnMut(&CString),
-{
-
-    let mut curr_ptr: uint = buf as uint;
-    let mut ctr = 0;
-    let (limited_count, limit) = match count {
-        Some(limit) => (true, limit),
-        None => (false, 0)
-    };
-    while ((limited_count && ctr < limit) || !limited_count)
-          && *(curr_ptr as *const libc::c_char) != 0 as libc::c_char {
-        let cstr = CString::new(curr_ptr as *const libc::c_char, false);
-        f(&cstr);
-        curr_ptr += cstr.len() + 1;
-        ctr += 1;
-    }
-    return ctr;
-}
-
-#[cfg(test)]
-mod tests {
-    use prelude::v1::*;
-    use super::*;
-    use ptr;
-    use thread::Thread;
-    use libc;
-
-    #[test]
-    fn test_str_multistring_parsing() {
-        unsafe {
-            let input = b"zero\0one\0\0";
-            let ptr = input.as_ptr();
-            let expected = ["zero", "one"];
-            let mut it = expected.iter();
-            let result = from_c_multistring(ptr as *const libc::c_char, None, |c| {
-                let cbytes = c.as_bytes_no_nul();
-                assert_eq!(cbytes, it.next().unwrap().as_bytes());
-            });
-            assert_eq!(result, 2);
-            assert!(it.next().is_none());
-        }
-    }
-
-    #[test]
-    fn test_str_to_c_str() {
-        let c_str = "".to_c_str();
-        unsafe {
-            assert_eq!(*c_str.as_ptr().offset(0), 0);
-        }
-
-        let c_str = "hello".to_c_str();
-        let buf = c_str.as_ptr();
-        unsafe {
-            assert_eq!(*buf.offset(0), 'h' as libc::c_char);
-            assert_eq!(*buf.offset(1), 'e' as libc::c_char);
-            assert_eq!(*buf.offset(2), 'l' as libc::c_char);
-            assert_eq!(*buf.offset(3), 'l' as libc::c_char);
-            assert_eq!(*buf.offset(4), 'o' as libc::c_char);
-            assert_eq!(*buf.offset(5), 0);
-        }
-    }
-
-    #[test]
-    fn test_vec_to_c_str() {
-        let b: &[u8] = &[];
-        let c_str = b.to_c_str();
-        unsafe {
-            assert_eq!(*c_str.as_ptr().offset(0), 0);
-        }
-
-        let c_str = b"hello".to_c_str();
-        let buf = c_str.as_ptr();
-        unsafe {
-            assert_eq!(*buf.offset(0), 'h' as libc::c_char);
-            assert_eq!(*buf.offset(1), 'e' as libc::c_char);
-            assert_eq!(*buf.offset(2), 'l' as libc::c_char);
-            assert_eq!(*buf.offset(3), 'l' as libc::c_char);
-            assert_eq!(*buf.offset(4), 'o' as libc::c_char);
-            assert_eq!(*buf.offset(5), 0);
-        }
-
-        let c_str = b"foo\xFF".to_c_str();
-        let buf = c_str.as_ptr();
-        unsafe {
-            assert_eq!(*buf.offset(0), 'f' as libc::c_char);
-            assert_eq!(*buf.offset(1), 'o' as libc::c_char);
-            assert_eq!(*buf.offset(2), 'o' as libc::c_char);
-            assert_eq!(*buf.offset(3), 0xffu8 as libc::c_char);
-            assert_eq!(*buf.offset(4), 0);
-        }
-    }
-
-    #[test]
-    fn test_unwrap() {
-        let c_str = "hello".to_c_str();
-        unsafe { libc::free(c_str.into_inner() as *mut libc::c_void) }
-    }
-
-    #[test]
-    fn test_as_ptr() {
-        let c_str = "hello".to_c_str();
-        let len = unsafe { libc::strlen(c_str.as_ptr()) };
-        assert_eq!(len, 5);
-    }
-
-    #[test]
-    fn test_iterator() {
-        let c_str = "".to_c_str();
-        let mut iter = c_str.iter();
-        assert_eq!(iter.next(), None);
-
-        let c_str = "hello".to_c_str();
-        let mut iter = c_str.iter();
-        assert_eq!(iter.next(), Some('h' as libc::c_char));
-        assert_eq!(iter.next(), Some('e' as libc::c_char));
-        assert_eq!(iter.next(), Some('l' as libc::c_char));
-        assert_eq!(iter.next(), Some('l' as libc::c_char));
-        assert_eq!(iter.next(), Some('o' as libc::c_char));
-        assert_eq!(iter.next(), None);
-    }
-
-    #[test]
-    fn test_to_c_str_fail() {
-        assert!(Thread::spawn(move|| { "he\x00llo".to_c_str() }).join().is_err());
-    }
-
-    #[test]
-    fn test_to_c_str_unchecked() {
-        unsafe {
-            let c_string = "he\x00llo".to_c_str_unchecked();
-            let buf = c_string.as_ptr();
-            assert_eq!(*buf.offset(0), 'h' as libc::c_char);
-            assert_eq!(*buf.offset(1), 'e' as libc::c_char);
-            assert_eq!(*buf.offset(2), 0);
-            assert_eq!(*buf.offset(3), 'l' as libc::c_char);
-            assert_eq!(*buf.offset(4), 'l' as libc::c_char);
-            assert_eq!(*buf.offset(5), 'o' as libc::c_char);
-            assert_eq!(*buf.offset(6), 0);
-        }
-    }
-
-    #[test]
-    fn test_as_bytes() {
-        let c_str = "hello".to_c_str();
-        assert_eq!(c_str.as_bytes(), b"hello\0");
-        let c_str = "".to_c_str();
-        assert_eq!(c_str.as_bytes(), b"\0");
-        let c_str = b"foo\xFF".to_c_str();
-        assert_eq!(c_str.as_bytes(), b"foo\xFF\0");
-    }
-
-    #[test]
-    fn test_as_bytes_no_nul() {
-        let c_str = "hello".to_c_str();
-        assert_eq!(c_str.as_bytes_no_nul(), b"hello");
-        let c_str = "".to_c_str();
-        let exp: &[u8] = &[];
-        assert_eq!(c_str.as_bytes_no_nul(), exp);
-        let c_str = b"foo\xFF".to_c_str();
-        assert_eq!(c_str.as_bytes_no_nul(), b"foo\xFF");
-    }
-
-    #[test]
-    fn test_as_str() {
-        let c_str = "hello".to_c_str();
-        assert_eq!(c_str.as_str(), Some("hello"));
-        let c_str = "".to_c_str();
-        assert_eq!(c_str.as_str(), Some(""));
-        let c_str = b"foo\xFF".to_c_str();
-        assert_eq!(c_str.as_str(), None);
-    }
-
-    #[test]
-    #[should_fail]
-    fn test_new_fail() {
-        let _c_str = unsafe { CString::new(ptr::null(), false) };
-    }
-
-    #[test]
-    fn test_clone() {
-        let a = "hello".to_c_str();
-        let b = a.clone();
-        assert!(a == b);
-    }
-
-    #[test]
-    fn test_clone_noleak() {
-        fn foo<F>(f: F) where F: FnOnce(&CString) {
-            let s = "test".to_string();
-            let c = s.to_c_str();
-            // give the closure a non-owned CString
-            let mut c_ = unsafe { CString::new(c.as_ptr(), false) };
-            f(&c_);
-            // muck with the buffer for later printing
-            unsafe { *c_.as_mut_ptr() = 'X' as libc::c_char }
-        }
-
-        let mut c_: Option<CString> = None;
-        foo(|c| {
-            c_ = Some(c.clone());
-            c.clone();
-            // force a copy, reading the memory
-            c.as_bytes().to_vec();
-        });
-        let c_ = c_.unwrap();
-        // force a copy, reading the memory
-        c_.as_bytes().to_vec();
-    }
-}
-
-#[cfg(test)]
-mod bench {
-    extern crate test;
-
-    use prelude::v1::*;
-    use self::test::Bencher;
-    use libc;
-    use c_str::ToCStr;
-
-    #[inline]
-    fn check(s: &str, c_str: *const libc::c_char) {
-        let s_buf = s.as_ptr();
-        for i in range(0, s.len()) {
-            unsafe {
-                assert_eq!(
-                    *s_buf.offset(i as int) as libc::c_char,
-                    *c_str.offset(i as int));
-            }
-        }
-    }
-
-    static S_SHORT: &'static str = "Mary";
-    static S_MEDIUM: &'static str = "Mary had a little lamb";
-    static S_LONG: &'static str = "\
-        Mary had a little lamb, Little lamb
-        Mary had a little lamb, Little lamb
-        Mary had a little lamb, Little lamb
-        Mary had a little lamb, Little lamb
-        Mary had a little lamb, Little lamb
-        Mary had a little lamb, Little lamb";
-
-    fn bench_to_string(b: &mut Bencher, s: &str) {
-        b.iter(|| {
-            let c_str = s.to_c_str();
-            check(s, c_str.as_ptr());
-        })
-    }
-
-    #[bench]
-    fn bench_to_c_str_short(b: &mut Bencher) {
-        bench_to_string(b, S_SHORT)
-    }
-
-    #[bench]
-    fn bench_to_c_str_medium(b: &mut Bencher) {
-        bench_to_string(b, S_MEDIUM)
-    }
-
-    #[bench]
-    fn bench_to_c_str_long(b: &mut Bencher) {
-        bench_to_string(b, S_LONG)
-    }
-
-    fn bench_to_c_str_unchecked(b: &mut Bencher, s: &str) {
-        b.iter(|| {
-            let c_str = unsafe { s.to_c_str_unchecked() };
-            check(s, c_str.as_ptr())
-        })
-    }
-
-    #[bench]
-    fn bench_to_c_str_unchecked_short(b: &mut Bencher) {
-        bench_to_c_str_unchecked(b, S_SHORT)
-    }
-
-    #[bench]
-    fn bench_to_c_str_unchecked_medium(b: &mut Bencher) {
-        bench_to_c_str_unchecked(b, S_MEDIUM)
-    }
-
-    #[bench]
-    fn bench_to_c_str_unchecked_long(b: &mut Bencher) {
-        bench_to_c_str_unchecked(b, S_LONG)
-    }
-
-    fn bench_with_c_str(b: &mut Bencher, s: &str) {
-        b.iter(|| {
-            s.with_c_str(|c_str_buf| check(s, c_str_buf))
-        })
-    }
-
-    #[bench]
-    fn bench_with_c_str_short(b: &mut Bencher) {
-        bench_with_c_str(b, S_SHORT)
-    }
-
-    #[bench]
-    fn bench_with_c_str_medium(b: &mut Bencher) {
-        bench_with_c_str(b, S_MEDIUM)
-    }
-
-    #[bench]
-    fn bench_with_c_str_long(b: &mut Bencher) {
-        bench_with_c_str(b, S_LONG)
-    }
-
-    fn bench_with_c_str_unchecked(b: &mut Bencher, s: &str) {
-        b.iter(|| {
-            unsafe {
-                s.with_c_str_unchecked(|c_str_buf| check(s, c_str_buf))
-            }
-        })
-    }
-
-    #[bench]
-    fn bench_with_c_str_unchecked_short(b: &mut Bencher) {
-        bench_with_c_str_unchecked(b, S_SHORT)
-    }
-
-    #[bench]
-    fn bench_with_c_str_unchecked_medium(b: &mut Bencher) {
-        bench_with_c_str_unchecked(b, S_MEDIUM)
-    }
-
-    #[bench]
-    fn bench_with_c_str_unchecked_long(b: &mut Bencher) {
-        bench_with_c_str_unchecked(b, S_LONG)
-    }
-}
diff --git a/src/libstd/c_vec.rs b/src/libstd/c_vec.rs
deleted file mode 100644
index 4a20208f31a..00000000000
--- a/src/libstd/c_vec.rs
+++ /dev/null
@@ -1,232 +0,0 @@
-// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-//! Library to interface with chunks of memory allocated in C.
-//!
-//! It is often desirable to safely interface with memory allocated from C,
-//! encapsulating the unsafety into allocation and destruction time.  Indeed,
-//! allocating memory externally is currently the only way to give Rust shared
-//! mut state with C programs that keep their own references; vectors are
-//! unsuitable because they could be reallocated or moved at any time, and
-//! importing C memory into a vector takes a one-time snapshot of the memory.
-//!
-//! This module simplifies the usage of such external blocks of memory.  Memory
-//! is encapsulated into an opaque object after creation; the lifecycle of the
-//! memory can be optionally managed by Rust, if an appropriate destructor
-//! closure is provided.  Safety is ensured by bounds-checking accesses, which
-//! are marshalled through get and set functions.
-//!
-//! There are three unsafe functions: the two constructors, and the
-//! unwrap method. The constructors are unsafe for the
-//! obvious reason (they act on a pointer that cannot be checked inside the
-//! method), but `unwrap()` is somewhat more subtle in its unsafety.
-//! It returns the contained pointer, but at the same time destroys the CVec
-//! without running its destructor. This can be used to pass memory back to
-//! C, but care must be taken that the ownership of underlying resources are
-//! handled correctly, i.e. that allocated memory is eventually freed
-//! if necessary.
-
-#![experimental]
-
-use kinds::Send;
-use mem;
-use ops::{Drop, FnOnce};
-use option::Option;
-use option::Option::{Some, None};
-use ptr::PtrExt;
-use ptr;
-use raw;
-use slice::AsSlice;
-use thunk::{Thunk};
-
-/// The type representing a foreign chunk of memory
-pub struct CVec<T> {
-    base: *mut T,
-    len: uint,
-    dtor: Option<Thunk>,
-}
-
-#[unsafe_destructor]
-impl<T> Drop for CVec<T> {
-    fn drop(&mut self) {
-        match self.dtor.take() {
-            None => (),
-            Some(f) => f.invoke(())
-        }
-    }
-}
-
-impl<T> CVec<T> {
-    /// Create a `CVec` from a raw pointer to a buffer with a given length.
-    ///
-    /// Panics if the given pointer is null. The returned vector will not attempt
-    /// to deallocate the vector when dropped.
-    ///
-    /// # Arguments
-    ///
-    /// * base - A raw pointer to a buffer
-    /// * len - The number of elements in the buffer
-    pub unsafe fn new(base: *mut T, len: uint) -> CVec<T> {
-        assert!(base != ptr::null_mut());
-        CVec {
-            base: base,
-            len: len,
-            dtor: None,
-        }
-    }
-
-    /// Create a `CVec` from a foreign buffer, with a given length,
-    /// and a function to run upon destruction.
-    ///
-    /// Panics if the given pointer is null.
-    ///
-    /// # Arguments
-    ///
-    /// * base - A foreign pointer to a buffer
-    /// * len - The number of elements in the buffer
-    /// * dtor - A fn to run when the value is destructed, useful
-    ///          for freeing the buffer, etc.
-    pub unsafe fn new_with_dtor<F>(base: *mut T,
-                                   len: uint,
-                                   dtor: F)
-                                   -> CVec<T>
-        where F : FnOnce(), F : Send
-    {
-        assert!(base != ptr::null_mut());
-        let dtor: Thunk = Thunk::new(dtor);
-        CVec {
-            base: base,
-            len: len,
-            dtor: Some(dtor)
-        }
-    }
-
-    /// View the stored data as a mutable slice.
-    pub fn as_mut_slice<'a>(&'a mut self) -> &'a mut [T] {
-        unsafe {
-            mem::transmute(raw::Slice { data: self.base as *const T, len: self.len })
-        }
-    }
-
-    /// Retrieves an element at a given index, returning `None` if the requested
-    /// index is greater than the length of the vector.
-    pub fn get<'a>(&'a self, ofs: uint) -> Option<&'a T> {
-        if ofs < self.len {
-            Some(unsafe { &*self.base.offset(ofs as int) })
-        } else {
-            None
-        }
-    }
-
-    /// Retrieves a mutable element at a given index, returning `None` if the
-    /// requested index is greater than the length of the vector.
-    pub fn get_mut<'a>(&'a mut self, ofs: uint) -> Option<&'a mut T> {
-        if ofs < self.len {
-            Some(unsafe { &mut *self.base.offset(ofs as int) })
-        } else {
-            None
-        }
-    }
-
-    /// Unwrap the pointer without running the destructor
-    ///
-    /// This method retrieves the underlying pointer, and in the process
-    /// destroys the CVec but without running the destructor. A use case
-    /// would be transferring ownership of the buffer to a C function, as
-    /// in this case you would not want to run the destructor.
-    ///
-    /// Note that if you want to access the underlying pointer without
-    /// cancelling the destructor, you can simply call `transmute` on the return
-    /// value of `get(0)`.
-    pub unsafe fn into_inner(mut self) -> *mut T {
-        self.dtor = None;
-        self.base
-    }
-
-    /// Returns the number of items in this vector.
-    pub fn len(&self) -> uint { self.len }
-
-    /// Returns whether this vector is empty.
-    pub fn is_empty(&self) -> bool { self.len() == 0 }
-}
-
-impl<T> AsSlice<T> for CVec<T> {
-    /// View the stored data as a slice.
-    fn as_slice<'a>(&'a self) -> &'a [T] {
-        unsafe {
-            mem::transmute(raw::Slice { data: self.base as *const T, len: self.len })
-        }
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use prelude::v1::*;
-
-    use super::CVec;
-    use libc;
-    use ptr;
-
-    fn malloc(n: uint) -> CVec<u8> {
-        unsafe {
-            let mem = ptr::Unique(libc::malloc(n as libc::size_t));
-            if mem.0.is_null() { ::alloc::oom() }
-
-            CVec::new_with_dtor(mem.0 as *mut u8,
-                                n,
-                                move|| { libc::free(mem.0 as *mut libc::c_void); })
-        }
-    }
-
-    #[test]
-    fn test_basic() {
-        let mut cv = malloc(16);
-
-        *cv.get_mut(3).unwrap() = 8;
-        *cv.get_mut(4).unwrap() = 9;
-        assert_eq!(*cv.get(3).unwrap(), 8);
-        assert_eq!(*cv.get(4).unwrap(), 9);
-        assert_eq!(cv.len(), 16);
-    }
-
-    #[test]
-    #[should_fail]
-    fn test_panic_at_null() {
-        unsafe {
-            CVec::new(ptr::null_mut::<u8>(), 9);
-        }
-    }
-
-    #[test]
-    fn test_overrun_get() {
-        let cv = malloc(16);
-
-        assert!(cv.get(17).is_none());
-    }
-
-    #[test]
-    fn test_overrun_set() {
-        let mut cv = malloc(16);
-
-        assert!(cv.get_mut(17).is_none());
-    }
-
-    #[test]
-    fn test_unwrap() {
-        unsafe {
-            let cv = CVec::new_with_dtor(1 as *mut int,
-                                         0,
-                                         move|:| panic!("Don't run this destructor!"));
-            let p = cv.into_inner();
-            assert_eq!(p, 1 as *mut int);
-        }
-    }
-
-}
diff --git a/src/libstd/dynamic_lib.rs b/src/libstd/dynamic_lib.rs
index de3d75ffb32..66cb1f2c948 100644
--- a/src/libstd/dynamic_lib.rs
+++ b/src/libstd/dynamic_lib.rs
@@ -17,7 +17,7 @@
 
 use prelude::v1::*;
 
-use c_str::ToCStr;
+use ffi::CString;
 use mem;
 use os;
 use str;
@@ -51,13 +51,11 @@ impl DynamicLibrary {
 
     /// Lazily open a dynamic library. When passed None it gives a
     /// handle to the calling process
-    pub fn open<T: ToCStr>(filename: Option<T>)
-                        -> Result<DynamicLibrary, String> {
+    pub fn open(filename: Option<&Path>) -> Result<DynamicLibrary, String> {
         unsafe {
-            let mut filename = filename;
             let maybe_library = dl::check_for_errors_in(|| {
-                match filename.take() {
-                    Some(name) => dl::open_external(name),
+                match filename {
+                    Some(name) => dl::open_external(name.as_vec()),
                     None => dl::open_internal()
                 }
             });
@@ -131,9 +129,8 @@ impl DynamicLibrary {
         // T but that feature is still unimplemented
 
         let maybe_symbol_value = dl::check_for_errors_in(|| {
-            symbol.with_c_str(|raw_string| {
-                dl::symbol(self.handle, raw_string)
-            })
+            let raw_string = CString::from_slice(symbol.as_bytes());
+            dl::symbol(self.handle, raw_string.as_ptr())
         });
 
         // The value must not be constructed if there is an error so
@@ -157,7 +154,7 @@ mod test {
     fn test_loading_cosine() {
         // The math library does not need to be loaded since it is already
         // statically linked in
-        let none: Option<Path> = None; // appease the typechecker
+        let none: Option<&Path> = None; // appease the typechecker
         let libm = match DynamicLibrary::open(none) {
             Err(error) => panic!("Could not load self as module: {}", error),
             Ok(libm) => libm
@@ -202,17 +199,17 @@ mod test {
           target_os = "freebsd",
           target_os = "dragonfly"))]
 pub mod dl {
-    use self::Rtld::*;
-
+    pub use self::Rtld::*;
     use prelude::v1::*;
-    use c_str::{CString, ToCStr};
+
+    use ffi::{self, CString};
+    use str;
     use libc;
     use ptr;
 
-    pub unsafe fn open_external<T: ToCStr>(filename: T) -> *mut u8 {
-        filename.with_c_str(|raw_name| {
-            dlopen(raw_name, Lazy as libc::c_int) as *mut u8
-        })
+    pub unsafe fn open_external(filename: &[u8]) -> *mut u8 {
+        let s = CString::from_slice(filename);
+        dlopen(s.as_ptr(), Lazy as libc::c_int) as *mut u8
     }
 
     pub unsafe fn open_internal() -> *mut u8 {
@@ -236,8 +233,8 @@ pub mod dl {
             let ret = if ptr::null() == last_error {
                 Ok(result)
             } else {
-                Err(String::from_str(CString::new(last_error, false).as_str()
-                    .unwrap()))
+                let s = ffi::c_str_to_bytes(&last_error);
+                Err(str::from_utf8(s).unwrap().to_string())
             };
 
             ret
@@ -273,7 +270,6 @@ pub mod dl {
 
 #[cfg(target_os = "windows")]
 pub mod dl {
-    use c_str::ToCStr;
     use iter::IteratorExt;
     use libc;
     use ops::FnOnce;
@@ -287,10 +283,9 @@ pub mod dl {
     use string::String;
     use vec::Vec;
 
-    pub unsafe fn open_external<T: ToCStr>(filename: T) -> *mut u8 {
+    pub unsafe fn open_external(filename: &[u8]) -> *mut u8 {
         // Windows expects Unicode data
-        let filename_cstr = filename.to_c_str();
-        let filename_str = str::from_utf8(filename_cstr.as_bytes_no_nul()).unwrap();
+        let filename_str = str::from_utf8(filename).unwrap();
         let mut filename_str: Vec<u16> = filename_str.utf16_units().collect();
         filename_str.push(0);
         LoadLibraryW(filename_str.as_ptr() as *const libc::c_void) as *mut u8
diff --git a/src/libstd/ffi/c_str.rs b/src/libstd/ffi/c_str.rs
new file mode 100644
index 00000000000..bef2344d9e8
--- /dev/null
+++ b/src/libstd/ffi/c_str.rs
@@ -0,0 +1,218 @@
+// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use fmt;
+use iter::IteratorExt;
+use libc;
+use mem;
+use ops::Deref;
+use slice::{self, SliceExt, AsSlice};
+use string::String;
+use vec::Vec;
+
+/// A type representing a C-compatible string
+///
+/// This type serves the primary purpose of being able to generate a
+/// C-compatible string from a Rust byte slice or vector. An instance of this
+/// type is a static guarantee that the underlying bytes contain no interior 0
+/// bytes and the final byte is 0.
+///
+/// A `CString` is created from either a byte slice or a byte vector. After
+/// being created, a `CString` predominately inherits all of its methods from
+/// the `Deref` implementation to `[libc::c_char]`. Note that the underlying
+/// array is represented as an array of `libc::c_char` as opposed to `u8`. A
+/// `u8` slice can be obtained with the `as_bytes` method.  Slices produced from
+/// a `CString` do *not* contain the trailing nul terminator unless otherwise
+/// specified.
+///
+/// # Example
+///
+/// ```no_run
+/// # extern crate libc;
+/// # fn main() {
+/// use std::ffi::CString;
+/// use libc;
+///
+/// extern {
+///     fn my_printer(s: *const libc::c_char);
+/// }
+///
+/// let to_print = "Hello, world!";
+/// let c_to_print = CString::from_slice(to_print.as_bytes());
+/// unsafe {
+///     my_printer(c_to_print.as_ptr());
+/// }
+/// # }
+/// ```
+#[derive(Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
+pub struct CString {
+    inner: Vec<libc::c_char>,
+}
+
+impl CString {
+    /// Create a new C-compatible string from a byte slice.
+    ///
+    /// This method will copy the data of the slice provided into a new
+    /// allocation, ensuring that there is a trailing 0 byte.
+    ///
+    /// # Panics
+    ///
+    /// This function will panic if there are any 0 bytes already in the slice
+    /// provided.
+    pub fn from_slice(v: &[u8]) -> CString {
+        CString::from_vec(v.to_vec())
+    }
+
+    /// Create a C-compatible string from a byte vector.
+    ///
+    /// This method will consume ownership of the provided vector, appending a 0
+    /// byte to the end after verifying that there are no interior 0 bytes.
+    ///
+    /// # Panics
+    ///
+    /// This function will panic if there are any 0 bytes already in the vector
+    /// provided.
+    pub fn from_vec(v: Vec<u8>) -> CString {
+        assert!(!v.iter().any(|&x| x == 0));
+        unsafe { CString::from_vec_unchecked(v) }
+    }
+
+    /// Create a C-compatibel string from a byte vector without checking for
+    /// interior 0 bytes.
+    ///
+    /// This method is equivalent to `from_vec` except that no runtime assertion
+    /// is made that `v` contains no 0 bytes.
+    pub unsafe fn from_vec_unchecked(mut v: Vec<u8>) -> CString {
+        v.push(0);
+        CString { inner: mem::transmute(v) }
+    }
+
+    /// Create a view into this C string which includes the trailing nul
+    /// terminator at the end of the string.
+    pub fn as_slice_with_nul(&self) -> &[libc::c_char] { self.inner.as_slice() }
+
+    /// Similar to the `as_slice` method, but returns a `u8` slice instead of a
+    /// `libc::c_char` slice.
+    pub fn as_bytes(&self) -> &[u8] {
+        unsafe { mem::transmute(self.as_slice()) }
+    }
+
+    /// Equivalend to `as_slice_with_nul` except that the type returned is a
+    /// `u8` slice instead of a `libc::c_char` slice.
+    pub fn as_bytes_with_nul(&self) -> &[u8] {
+        unsafe { mem::transmute(self.as_slice_with_nul()) }
+    }
+}
+
+impl Deref for CString {
+    type Target = [libc::c_char];
+
+    fn deref(&self) -> &[libc::c_char] {
+        self.inner.slice_to(self.inner.len() - 1)
+    }
+}
+
+impl fmt::Show for CString {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        String::from_utf8_lossy(self.as_bytes()).fmt(f)
+    }
+}
+
+/// Interpret a C string as a byte slice.
+///
+/// This function will calculate the length of the C string provided, and it
+/// will then return a corresponding slice for the contents of the C string not
+/// including the nul terminator.
+///
+/// This function will tie the lifetime of the returned slice to the lifetime of
+/// the pointer provided. This is done to help prevent the slice from escaping
+/// the lifetime of the pointer itself. If a longer lifetime is needed, then
+/// `mem::copy_lifetime` should be used.
+///
+/// This function is unsafe because there is no guarantee of the validity of the
+/// pointer `raw` or a guarantee that a nul terminator will be found.
+///
+/// # Example
+///
+/// ```no_run
+/// # extern crate libc;
+/// # fn main() {
+/// use std::ffi;
+/// use std::str;
+/// use libc;
+///
+/// extern {
+///     fn my_string() -> *const libc::c_char;
+/// }
+///
+/// unsafe {
+///     let to_print = my_string();
+///     let slice = ffi::c_str_to_bytes(&to_print);
+///     println!("string returned: {}", str::from_utf8(slice).unwrap());
+/// }
+/// # }
+/// ```
+pub unsafe fn c_str_to_bytes<'a>(raw: &'a *const libc::c_char) -> &'a [u8] {
+    let len = libc::strlen(*raw);
+    slice::from_raw_buf(&*(raw as *const _ as *const *const u8), len as uint)
+}
+
+/// Interpret a C string as a byte slice with the nul terminator.
+///
+/// This function is identical to `from_raw_buf` except that the returned slice
+/// will include the nul terminator of the string.
+pub unsafe fn c_str_to_bytes_with_nul<'a>(raw: &'a *const libc::c_char) -> &'a [u8] {
+    let len = libc::strlen(*raw) + 1;
+    slice::from_raw_buf(&*(raw as *const _ as *const *const u8), len as uint)
+}
+
+#[cfg(test)]
+mod tests {
+    use prelude::v1::*;
+    use super::*;
+    use libc;
+    use mem;
+
+    #[test]
+    fn c_to_rust() {
+        let data = b"123\0";
+        let ptr = data.as_ptr() as *const libc::c_char;
+        unsafe {
+            assert_eq!(c_str_to_bytes(&ptr), b"123");
+            assert_eq!(c_str_to_bytes_with_nul(&ptr), b"123\0");
+        }
+    }
+
+    #[test]
+    fn simple() {
+        let s = CString::from_slice(b"1234");
+        assert_eq!(s.as_bytes(), b"1234");
+        assert_eq!(s.as_bytes_with_nul(), b"1234\0");
+        unsafe {
+            assert_eq!(s.as_slice(),
+                       mem::transmute::<_, &[libc::c_char]>(b"1234"));
+            assert_eq!(s.as_slice_with_nul(),
+                       mem::transmute::<_, &[libc::c_char]>(b"1234\0"));
+        }
+    }
+
+    #[should_fail] #[test]
+    fn build_with_zero1() { CString::from_slice(b"\0"); }
+    #[should_fail] #[test]
+    fn build_with_zero2() { CString::from_vec(vec![0]); }
+
+    #[test]
+    fn build_with_zero3() {
+        unsafe {
+            let s = CString::from_vec_unchecked(vec![0]);
+            assert_eq!(s.as_bytes(), b"\0");
+        }
+    }
+}
diff --git a/src/libstd/ffi/mod.rs b/src/libstd/ffi/mod.rs
new file mode 100644
index 00000000000..cc86f804e3e
--- /dev/null
+++ b/src/libstd/ffi/mod.rs
@@ -0,0 +1,20 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! Utilities related to FFI bindings.
+
+#![unstable = "module just underwent fairly large reorganization and the dust \
+               still needs to settle"]
+
+pub use self::c_str::CString;
+pub use self::c_str::c_str_to_bytes;
+pub use self::c_str::c_str_to_bytes_with_nul;
+
+mod c_str;
diff --git a/src/libstd/io/net/pipe.rs b/src/libstd/io/net/pipe.rs
index daefdd28b30..738c70412f7 100644
--- a/src/libstd/io/net/pipe.rs
+++ b/src/libstd/io/net/pipe.rs
@@ -22,7 +22,8 @@
 
 use prelude::v1::*;
 
-use c_str::ToCStr;
+use ffi::CString;
+use path::BytesContainer;
 use io::{Listener, Acceptor, IoResult, TimedOut, standard_error};
 use sys::pipe::UnixAcceptor as UnixAcceptorImp;
 use sys::pipe::UnixListener as UnixListenerImp;
@@ -53,8 +54,9 @@ impl UnixStream {
     /// let mut stream = UnixStream::connect(&server);
     /// stream.write(&[1, 2, 3]);
     /// ```
-    pub fn connect<P: ToCStr>(path: &P) -> IoResult<UnixStream> {
-        UnixStreamImp::connect(&path.to_c_str(), None)
+    pub fn connect<P: BytesContainer>(path: P) -> IoResult<UnixStream> {
+        let path = CString::from_slice(path.container_as_bytes());
+        UnixStreamImp::connect(&path, None)
             .map(|inner| UnixStream { inner: inner })
     }
 
@@ -67,13 +69,15 @@ impl UnixStream {
     /// If a `timeout` with zero or negative duration is specified then
     /// the function returns `Err`, with the error kind set to `TimedOut`.
     #[experimental = "the timeout argument is likely to change types"]
-    pub fn connect_timeout<P: ToCStr>(path: &P,
-                                      timeout: Duration) -> IoResult<UnixStream> {
+    pub fn connect_timeout<P>(path: P, timeout: Duration)
+                              -> IoResult<UnixStream>
+                              where P: BytesContainer {
         if timeout <= Duration::milliseconds(0) {
             return Err(standard_error(TimedOut));
         }
 
-        UnixStreamImp::connect(&path.to_c_str(), Some(timeout.num_milliseconds() as u64))
+        let path = CString::from_slice(path.container_as_bytes());
+        UnixStreamImp::connect(&path, Some(timeout.num_milliseconds() as u64))
             .map(|inner| UnixStream { inner: inner })
     }
 
@@ -177,8 +181,9 @@ impl UnixListener {
     /// }
     /// # }
     /// ```
-    pub fn bind<P: ToCStr>(path: &P) -> IoResult<UnixListener> {
-        UnixListenerImp::bind(&path.to_c_str())
+    pub fn bind<P: BytesContainer>(path: P) -> IoResult<UnixListener> {
+        let path = CString::from_slice(path.container_as_bytes());
+        UnixListenerImp::bind(&path)
             .map(|inner| UnixListener { inner: inner })
     }
 }
diff --git a/src/libstd/io/process.rs b/src/libstd/io/process.rs
index 5886c9cc3e2..ea232ad0c3f 100644
--- a/src/libstd/io/process.rs
+++ b/src/libstd/io/process.rs
@@ -18,8 +18,8 @@ pub use self::ProcessExit::*;
 
 use prelude::v1::*;
 
-use c_str::{CString, ToCStr};
 use collections::HashMap;
+use ffi::CString;
 use fmt;
 use hash::Hash;
 use io::pipe::{PipeStream, PipePair};
@@ -35,6 +35,7 @@ use sys;
 use thread::Thread;
 
 #[cfg(windows)] use std::hash::sip::SipState;
+#[cfg(windows)] use str;
 
 /// Signal a process to exit, without forcibly killing it. Corresponds to
 /// SIGTERM on unix platforms.
@@ -109,11 +110,11 @@ struct EnvKey(CString);
 impl Hash for EnvKey {
     fn hash(&self, state: &mut SipState) {
         let &EnvKey(ref x) = self;
-        match x.as_str() {
-            Some(s) => for ch in s.chars() {
+        match str::from_utf8(x.as_bytes()) {
+            Ok(s) => for ch in s.chars() {
                 (ch as u8 as char).to_lowercase().hash(state);
             },
-            None => x.hash(state)
+            Err(..) => x.hash(state)
         }
     }
 }
@@ -123,8 +124,8 @@ impl PartialEq for EnvKey {
     fn eq(&self, other: &EnvKey) -> bool {
         let &EnvKey(ref x) = self;
         let &EnvKey(ref y) = other;
-        match (x.as_str(), y.as_str()) {
-            (Some(xs), Some(ys)) => {
+        match (str::from_utf8(x.as_bytes()), str::from_utf8(y.as_bytes())) {
+            (Ok(xs), Ok(ys)) => {
                 if xs.len() != ys.len() {
                     return false
                 } else {
@@ -185,10 +186,10 @@ pub struct Command {
 }
 
 // FIXME (#12938): Until DST lands, we cannot decompose &str into & and str, so
-// we cannot usefully take ToCStr arguments by reference (without forcing an
+// we cannot usefully take BytesContainer arguments by reference (without forcing an
 // additional & around &str). So we are instead temporarily adding an instance
-// for &Path, so that we can take ToCStr as owned. When DST lands, the &Path
-// instance should be removed, and arguments bound by ToCStr should be passed by
+// for &Path, so that we can take BytesContainer as owned. When DST lands, the &Path
+// instance should be removed, and arguments bound by BytesContainer should be passed by
 // reference. (Here: {new, arg, args, env}.)
 
 impl Command {
@@ -203,9 +204,9 @@ impl Command {
     ///
     /// Builder methods are provided to change these defaults and
     /// otherwise configure the process.
-    pub fn new<T:ToCStr>(program: T) -> Command {
+    pub fn new<T: BytesContainer>(program: T) -> Command {
         Command {
-            program: program.to_c_str(),
+            program: CString::from_slice(program.container_as_bytes()),
             args: Vec::new(),
             env: None,
             cwd: None,
@@ -219,27 +220,29 @@ impl Command {
     }
 
     /// Add an argument to pass to the program.
-    pub fn arg<'a, T: ToCStr>(&'a mut self, arg: T) -> &'a mut Command {
-        self.args.push(arg.to_c_str());
+    pub fn arg<'a, T: BytesContainer>(&'a mut self, arg: T) -> &'a mut Command {
+        self.args.push(CString::from_slice(arg.container_as_bytes()));
         self
     }
 
     /// Add multiple arguments to pass to the program.
-    pub fn args<'a, T: ToCStr>(&'a mut self, args: &[T]) -> &'a mut Command {
-        self.args.extend(args.iter().map(|arg| arg.to_c_str()));;
+    pub fn args<'a, T: BytesContainer>(&'a mut self, args: &[T]) -> &'a mut Command {
+        self.args.extend(args.iter().map(|arg| {
+            CString::from_slice(arg.container_as_bytes())
+        }));
         self
     }
     // Get a mutable borrow of the environment variable map for this `Command`.
-    fn get_env_map<'a>(&'a mut self) -> &'a mut  EnvMap {
+    fn get_env_map<'a>(&'a mut self) -> &'a mut EnvMap {
         match self.env {
             Some(ref mut map) => map,
             None => {
                 // if the env is currently just inheriting from the parent's,
                 // materialize the parent's env into a hashtable.
-                self.env = Some(os::env_as_bytes().into_iter()
-                                   .map(|(k, v)| (EnvKey(k.to_c_str()),
-                                                  v.to_c_str()))
-                                   .collect());
+                self.env = Some(os::env_as_bytes().into_iter().map(|(k, v)| {
+                    (EnvKey(CString::from_slice(k.as_slice())),
+                     CString::from_slice(v.as_slice()))
+                }).collect());
                 self.env.as_mut().unwrap()
             }
         }
@@ -249,15 +252,20 @@ impl Command {
     ///
     /// Note that environment variable names are case-insensitive (but case-preserving) on Windows,
     /// and case-sensitive on all other platforms.
-    pub fn env<'a, T: ToCStr, U: ToCStr>(&'a mut self, key: T, val: U)
-                                         -> &'a mut Command {
-        self.get_env_map().insert(EnvKey(key.to_c_str()), val.to_c_str());
+    pub fn env<'a, T, U>(&'a mut self, key: T, val: U)
+                         -> &'a mut Command
+                         where T: BytesContainer, U: BytesContainer {
+        let key = EnvKey(CString::from_slice(key.container_as_bytes()));
+        let val = CString::from_slice(val.container_as_bytes());
+        self.get_env_map().insert(key, val);
         self
     }
 
     /// Removes an environment variable mapping.
-    pub fn env_remove<'a, T: ToCStr>(&'a mut self, key: T) -> &'a mut Command {
-        self.get_env_map().remove(&EnvKey(key.to_c_str()));
+    pub fn env_remove<'a, T>(&'a mut self, key: T) -> &'a mut Command
+                             where T: BytesContainer {
+        let key = EnvKey(CString::from_slice(key.container_as_bytes()));
+        self.get_env_map().remove(&key);
         self
     }
 
@@ -265,16 +273,19 @@ impl Command {
     ///
     /// If the given slice contains multiple instances of an environment
     /// variable, the *rightmost* instance will determine the value.
-    pub fn env_set_all<'a, T: ToCStr, U: ToCStr>(&'a mut self, env: &[(T,U)])
-                                                 -> &'a mut Command {
-        self.env = Some(env.iter().map(|&(ref k, ref v)| (EnvKey(k.to_c_str()), v.to_c_str()))
-                                  .collect());
+    pub fn env_set_all<'a, T, U>(&'a mut self, env: &[(T,U)])
+                                 -> &'a mut Command
+                                 where T: BytesContainer, U: BytesContainer {
+        self.env = Some(env.iter().map(|&(ref k, ref v)| {
+            (EnvKey(CString::from_slice(k.container_as_bytes())),
+             CString::from_slice(v.container_as_bytes()))
+        }).collect());
         self
     }
 
     /// Set the working directory for the child process.
     pub fn cwd<'a>(&'a mut self, dir: &Path) -> &'a mut Command {
-        self.cwd = Some(dir.to_c_str());
+        self.cwd = Some(CString::from_slice(dir.as_vec()));
         self
     }
 
@@ -389,9 +400,9 @@ impl fmt::Show for Command {
     /// non-utf8 data is lossily converted using the utf8 replacement
     /// character.
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        try!(write!(f, "{}", String::from_utf8_lossy(self.program.as_bytes_no_nul())));
+        try!(write!(f, "{}", String::from_utf8_lossy(self.program.as_bytes())));
         for arg in self.args.iter() {
-            try!(write!(f, " '{}'", String::from_utf8_lossy(arg.as_bytes_no_nul())));
+            try!(write!(f, " '{}'", String::from_utf8_lossy(arg.as_bytes())));
         }
         Ok(())
     }
@@ -1208,13 +1219,13 @@ mod tests {
     #[test]
     #[cfg(windows)]
     fn env_map_keys_ci() {
-        use c_str::ToCStr;
+        use ffi::CString;
         use super::EnvKey;
         let mut cmd = Command::new("");
         cmd.env("path", "foo");
         cmd.env("Path", "bar");
         let env = &cmd.env.unwrap();
-        let val = env.get(&EnvKey("PATH".to_c_str()));
-        assert!(val.unwrap() == &"bar".to_c_str());
+        let val = env.get(&EnvKey(CString::from_slice(b"PATH")));
+        assert!(val.unwrap() == &CString::from_slice(b"bar"));
     }
 }
diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs
index 608ad9882b9..2d3a4639379 100644
--- a/src/libstd/lib.rs
+++ b/src/libstd/lib.rs
@@ -208,10 +208,10 @@ pub mod num;
 
 /* Runtime and platform support */
 
-pub mod thread_local;
-pub mod c_str;
-pub mod c_vec;
+pub mod thread_local; // first for macros
+
 pub mod dynamic_lib;
+pub mod ffi;
 pub mod fmt;
 pub mod io;
 pub mod os;
diff --git a/src/libstd/os.rs b/src/libstd/os.rs
index be8f82349c2..300ceec4b45 100644
--- a/src/libstd/os.rs
+++ b/src/libstd/os.rs
@@ -57,12 +57,10 @@ use string::{String, ToString};
 use sync::atomic::{AtomicInt, ATOMIC_INT_INIT, Ordering};
 use vec::Vec;
 
-#[cfg(unix)] use c_str::ToCStr;
+#[cfg(unix)] use ffi::{self, CString};
 
-#[cfg(unix)]
-pub use sys::ext as unix;
-#[cfg(windows)]
-pub use sys::ext as windows;
+#[cfg(unix)] pub use sys::ext as unix;
+#[cfg(windows)] pub use sys::ext as windows;
 
 /// Get the number of cores available
 pub fn num_cpus() -> uint {
@@ -196,15 +194,14 @@ pub fn getenv(n: &str) -> Option<String> {
 ///
 /// Panics if `n` has any interior NULs.
 pub fn getenv_as_bytes(n: &str) -> Option<Vec<u8>> {
-    use c_str::CString;
-
     unsafe {
         with_env_lock(|| {
-            let s = n.with_c_str(|buf| libc::getenv(buf));
+            let s = CString::from_slice(n.as_bytes());
+            let s = libc::getenv(s.as_ptr()) as *const _;
             if s.is_null() {
                 None
             } else {
-                Some(CString::new(s as *const libc::c_char, false).as_bytes_no_nul().to_vec())
+                Some(ffi::c_str_to_bytes(&s).to_vec())
             }
         })
     }
@@ -253,13 +250,12 @@ pub fn setenv<T: BytesContainer>(n: &str, v: T) {
     fn _setenv(n: &str, v: &[u8]) {
         unsafe {
             with_env_lock(|| {
-                n.with_c_str(|nbuf| {
-                    v.with_c_str(|vbuf| {
-                        if libc::funcs::posix01::unistd::setenv(nbuf, vbuf, 1) != 0 {
-                            panic!(IoError::last_error());
-                        }
-                    })
-                })
+                let k = CString::from_slice(n.as_bytes());
+                let v = CString::from_slice(v);
+                if libc::funcs::posix01::unistd::setenv(k.as_ptr(),
+                                                        v.as_ptr(), 1) != 0 {
+                    panic!(IoError::last_error());
+                }
             })
         }
     }
@@ -289,11 +285,10 @@ pub fn unsetenv(n: &str) {
     fn _unsetenv(n: &str) {
         unsafe {
             with_env_lock(|| {
-                n.with_c_str(|nbuf| {
-                    if libc::funcs::posix01::unistd::unsetenv(nbuf) != 0 {
-                        panic!(IoError::last_error());
-                    }
-                })
+                let nbuf = CString::from_slice(n.as_bytes());
+                if libc::funcs::posix01::unistd::unsetenv(nbuf.as_ptr()) != 0 {
+                    panic!(IoError::last_error());
+                }
             })
         }
     }
@@ -618,11 +613,10 @@ pub fn get_exit_status() -> int {
 #[cfg(target_os = "macos")]
 unsafe fn load_argc_and_argv(argc: int,
                              argv: *const *const c_char) -> Vec<Vec<u8>> {
-    use c_str::CString;
     use iter::range;
 
     range(0, argc as uint).map(|i| {
-        CString::new(*argv.offset(i as int), false).as_bytes_no_nul().to_vec()
+        ffi::c_str_to_bytes(&*argv.offset(i as int)).to_vec()
     }).collect()
 }
 
@@ -652,7 +646,6 @@ fn real_args_as_bytes() -> Vec<Vec<u8>> {
 // res
 #[cfg(target_os = "ios")]
 fn real_args_as_bytes() -> Vec<Vec<u8>> {
-    use c_str::CString;
     use iter::range;
     use mem;
 
diff --git a/src/libstd/path/mod.rs b/src/libstd/path/mod.rs
index bf9ffbffe7d..2f014872402 100644
--- a/src/libstd/path/mod.rs
+++ b/src/libstd/path/mod.rs
@@ -62,7 +62,7 @@
 #![experimental]
 
 use core::kinds::Sized;
-use c_str::CString;
+use ffi::CString;
 use clone::Clone;
 use fmt;
 use iter::IteratorExt;
@@ -892,7 +892,7 @@ impl BytesContainer for Vec<u8> {
 impl BytesContainer for CString {
     #[inline]
     fn container_as_bytes<'a>(&'a self) -> &'a [u8] {
-        self.as_bytes_no_nul()
+        self.as_bytes()
     }
 }
 
@@ -913,21 +913,3 @@ impl<'a, Sized? T: BytesContainer> BytesContainer for &'a T {
 fn contains_nul<T: BytesContainer>(v: &T) -> bool {
     v.container_as_bytes().iter().any(|&x| x == 0)
 }
-
-#[cfg(test)]
-mod tests {
-    use prelude::v1::*;
-    use c_str::ToCStr;
-    use path::{WindowsPath, PosixPath};
-
-    #[test]
-    fn test_cstring() {
-        let input = "/foo/bar/baz";
-        let path: PosixPath = PosixPath::new(input.to_c_str());
-        assert_eq!(path.as_vec(), input.as_bytes());
-
-        let input = r"\foo\bar\baz";
-        let path: WindowsPath = WindowsPath::new(input.to_c_str());
-        assert_eq!(path.as_str().unwrap(), input);
-    }
-}
diff --git a/src/libstd/path/posix.rs b/src/libstd/path/posix.rs
index ae82e201cb8..013212b2705 100644
--- a/src/libstd/path/posix.rs
+++ b/src/libstd/path/posix.rs
@@ -10,19 +10,16 @@
 
 //! POSIX file path handling
 
-use c_str::{CString, ToCStr};
 use clone::Clone;
-use cmp::{PartialEq, Eq, PartialOrd, Ord, Ordering};
+use cmp::{Ordering, Eq, Ord, PartialEq, PartialOrd};
 use hash;
 use io::Writer;
 use iter::{AdditiveIterator, Extend};
 use iter::{Iterator, IteratorExt, Map};
-use option::Option;
-use option::Option::{None, Some};
 use kinds::Sized;
-use str::{FromStr, Str};
-use str;
-use slice::{Split, AsSlice, SliceConcatExt, SliceExt};
+use option::Option::{self, Some, None};
+use slice::{AsSlice, Split, SliceExt, SliceConcatExt};
+use str::{self, FromStr, StrExt};
 use vec::Vec;
 
 use super::{BytesContainer, GenericPath, GenericPathUnsafe};
@@ -86,26 +83,6 @@ impl FromStr for Path {
     }
 }
 
-// FIXME (#12938): Until DST lands, we cannot decompose &str into & and str, so
-// we cannot usefully take ToCStr arguments by reference (without forcing an
-// additional & around &str). So we are instead temporarily adding an instance
-// for &Path, so that we can take ToCStr as owned. When DST lands, the &Path
-// instance should be removed, and arguments bound by ToCStr should be passed by
-// reference.
-
-impl ToCStr for Path {
-    #[inline]
-    fn to_c_str(&self) -> CString {
-        // The Path impl guarantees no internal NUL
-        unsafe { self.to_c_str_unchecked() }
-    }
-
-    #[inline]
-    unsafe fn to_c_str_unchecked(&self) -> CString {
-        self.as_vec().to_c_str_unchecked()
-    }
-}
-
 impl<S: hash::Writer> hash::Hash<S> for Path {
     #[inline]
     fn hash(&self, state: &mut S) {
diff --git a/src/libstd/path/windows.rs b/src/libstd/path/windows.rs
index cf8bc0e6242..05129a7ab9d 100644
--- a/src/libstd/path/windows.rs
+++ b/src/libstd/path/windows.rs
@@ -15,17 +15,15 @@
 use self::PathPrefix::*;
 
 use ascii::AsciiExt;
-use c_str::{CString, ToCStr};
 use char::CharExt;
 use clone::Clone;
-use cmp::{PartialEq, Eq, PartialOrd, Ord, Ordering};
+use cmp::{Ordering, Eq, Ord, PartialEq, PartialOrd};
 use hash;
 use io::Writer;
 use iter::{AdditiveIterator, Extend};
 use iter::{Iterator, IteratorExt, Map, repeat};
 use mem;
-use option::Option;
-use option::Option::{Some, None};
+use option::Option::{self, Some, None};
 use slice::{SliceExt, SliceConcatExt};
 use str::{SplitTerminator, FromStr, StrExt};
 use string::{String, ToString};
@@ -112,26 +110,6 @@ impl FromStr for Path {
     }
 }
 
-// FIXME (#12938): Until DST lands, we cannot decompose &str into & and str, so
-// we cannot usefully take ToCStr arguments by reference (without forcing an
-// additional & around &str). So we are instead temporarily adding an instance
-// for &Path, so that we can take ToCStr as owned. When DST lands, the &Path
-// instance should be removed, and arguments bound by ToCStr should be passed by
-// reference.
-
-impl ToCStr for Path {
-    #[inline]
-    fn to_c_str(&self) -> CString {
-        // The Path impl guarantees no internal NUL
-        unsafe { self.to_c_str_unchecked() }
-    }
-
-    #[inline]
-    unsafe fn to_c_str_unchecked(&self) -> CString {
-        self.as_vec().to_c_str_unchecked()
-    }
-}
-
 impl<S: hash::Writer> hash::Hash<S> for Path {
     #[cfg(not(test))]
     #[inline]
diff --git a/src/libstd/rt/args.rs b/src/libstd/rt/args.rs
index 4734a39c835..86abacb9365 100644
--- a/src/libstd/rt/args.rs
+++ b/src/libstd/rt/args.rs
@@ -46,8 +46,9 @@ pub fn clone() -> Option<Vec<Vec<u8>>> { imp::clone() }
 mod imp {
     use prelude::v1::*;
 
+    use libc;
     use mem;
-    use slice;
+    use ffi;
 
     use sync::{StaticMutex, MUTEX_INIT};
 
@@ -95,13 +96,9 @@ mod imp {
     }
 
     unsafe fn load_argc_and_argv(argc: int, argv: *const *const u8) -> Vec<Vec<u8>> {
+        let argv = argv as *const *const libc::c_char;
         range(0, argc as uint).map(|i| {
-            let arg = *argv.offset(i as int);
-            let mut len = 0u;
-            while *arg.offset(len as int) != 0 {
-                len += 1u;
-            }
-            slice::from_raw_buf(&arg, len).to_vec()
+            ffi::c_str_to_bytes(&*argv.offset(i as int)).to_vec()
         }).collect()
     }
 
diff --git a/src/libstd/rt/backtrace.rs b/src/libstd/rt/backtrace.rs
index 578239c9cc4..bb0b6fe804b 100644
--- a/src/libstd/rt/backtrace.rs
+++ b/src/libstd/rt/backtrace.rs
@@ -15,7 +15,7 @@
 use prelude::v1::*;
 
 use os;
-use sync::atomic::{mod, Ordering};
+use sync::atomic::{self, Ordering};
 
 pub use sys::backtrace::write;
 
diff --git a/src/libstd/rt/unwind.rs b/src/libstd/rt/unwind.rs
index a48a8edd82f..71169386c18 100644
--- a/src/libstd/rt/unwind.rs
+++ b/src/libstd/rt/unwind.rs
@@ -67,7 +67,7 @@ use fmt;
 use intrinsics;
 use libc::c_void;
 use mem;
-use sync::atomic::{mod, Ordering};
+use sync::atomic::{self, Ordering};
 use sync::{Once, ONCE_INIT};
 
 use rt::libunwind as uw;
diff --git a/src/libstd/rt/util.rs b/src/libstd/rt/util.rs
index 883a01fa318..bc01ce926f8 100644
--- a/src/libstd/rt/util.rs
+++ b/src/libstd/rt/util.rs
@@ -19,7 +19,7 @@ use libc::{self, uintptr_t};
 use os;
 use slice;
 use str;
-use sync::atomic::{mod, Ordering};
+use sync::atomic::{self, Ordering};
 
 /// Dynamically inquire about whether we're running under V.
 /// You should usually not use this unless your test definitely
diff --git a/src/libstd/sys/common/mod.rs b/src/libstd/sys/common/mod.rs
index a441e55a732..a31dcc9884f 100644
--- a/src/libstd/sys/common/mod.rs
+++ b/src/libstd/sys/common/mod.rs
@@ -14,7 +14,7 @@
 use io::{self, IoError, IoResult};
 use prelude::v1::*;
 use sys::{last_error, retry};
-use c_str::CString;
+use ffi::CString;
 use num::Int;
 use path::BytesContainer;
 use collections;
diff --git a/src/libstd/sys/common/net.rs b/src/libstd/sys/common/net.rs
index 3f67b284f68..4cf891ac498 100644
--- a/src/libstd/sys/common/net.rs
+++ b/src/libstd/sys/common/net.rs
@@ -12,15 +12,16 @@ use prelude::v1::*;
 use self::SocketStatus::*;
 use self::InAddr::*;
 
-use c_str::ToCStr;
+use ffi::CString;
+use ffi;
 use io::net::addrinfo;
 use io::net::ip::{SocketAddr, IpAddr, Ipv4Addr, Ipv6Addr};
 use io::{IoResult, IoError};
 use libc::{self, c_char, c_int};
-use c_str::CString;
 use mem;
 use num::Int;
 use ptr::{self, null, null_mut};
+use str;
 use sys::{self, retry, c, sock_t, last_error, last_net_error, last_gai_error, close_sock,
           wrlen, msglen_t, os, wouldblock, set_nonblocking, timer, ms_to_timeval,
           decode_error_detailed};
@@ -234,9 +235,9 @@ pub fn get_host_addresses(host: Option<&str>, servname: Option<&str>,
 
     assert!(host.is_some() || servname.is_some());
 
-    let c_host = host.map(|x| x.to_c_str());
+    let c_host = host.map(|x| CString::from_slice(x.as_bytes()));
     let c_host = c_host.as_ref().map(|x| x.as_ptr()).unwrap_or(null());
-    let c_serv = servname.map(|x| x.to_c_str());
+    let c_serv = servname.map(|x| CString::from_slice(x.as_bytes()));
     let c_serv = c_serv.as_ref().map(|x| x.as_ptr()).unwrap_or(null());
 
     let hint = hint.map(|hint| {
@@ -324,7 +325,8 @@ pub fn get_address_name(addr: IpAddr) -> Result<String, IoError> {
     }
 
     unsafe {
-        Ok(CString::new(hostbuf.as_ptr(), false).as_str().unwrap().to_string())
+        Ok(str::from_utf8(ffi::c_str_to_bytes(&hostbuf.as_ptr()))
+               .unwrap().to_string())
     }
 }
 
diff --git a/src/libstd/sys/unix/backtrace.rs b/src/libstd/sys/unix/backtrace.rs
index 5b261ea6b9e..ca268a8f27f 100644
--- a/src/libstd/sys/unix/backtrace.rs
+++ b/src/libstd/sys/unix/backtrace.rs
@@ -83,12 +83,13 @@
 /// to symbols. This is a bit of a hokey implementation as-is, but it works for
 /// all unix platforms we support right now, so it at least gets the job done.
 
-use c_str::CString;
-use io::{IoResult, Writer};
+use prelude::v1::*;
+
+use ffi;
+use io::IoResult;
 use libc;
 use mem;
-use option::Option::{self, Some, None};
-use result::Result::{Ok, Err};
+use str;
 use sync::{StaticMutex, MUTEX_INIT};
 
 use sys_common::backtrace::*;
@@ -105,9 +106,7 @@ use sys_common::backtrace::*;
 #[cfg(all(target_os = "ios", target_arch = "arm"))]
 #[inline(never)]
 pub fn write(w: &mut Writer) -> IoResult<()> {
-    use iter::{IteratorExt, range};
     use result;
-    use slice::SliceExt;
 
     extern {
         fn backtrace(buf: *mut *mut libc::c_void,
@@ -234,19 +233,15 @@ fn print(w: &mut Writer, idx: int, addr: *mut libc::c_void) -> IoResult<()> {
         output(w, idx,addr, None)
     } else {
         output(w, idx, addr, Some(unsafe {
-            CString::new(info.dli_sname, false)
+            ffi::c_str_to_bytes(&info.dli_sname)
         }))
     }
 }
 
 #[cfg(not(any(target_os = "macos", target_os = "ios")))]
 fn print(w: &mut Writer, idx: int, addr: *mut libc::c_void) -> IoResult<()> {
-    use iter::{Iterator, IteratorExt};
     use os;
-    use path::GenericPath;
-    use ptr::PtrExt;
     use ptr;
-    use slice::SliceExt;
 
     ////////////////////////////////////////////////////////////////////////
     // libbacktrace.h API
@@ -368,15 +363,15 @@ fn print(w: &mut Writer, idx: int, addr: *mut libc::c_void) -> IoResult<()> {
     if ret == 0 || data.is_null() {
         output(w, idx, addr, None)
     } else {
-        output(w, idx, addr, Some(unsafe { CString::new(data, false) }))
+        output(w, idx, addr, Some(unsafe { ffi::c_str_to_bytes(&data) }))
     }
 }
 
 // Finally, after all that work above, we can emit a symbol.
 fn output(w: &mut Writer, idx: int, addr: *mut libc::c_void,
-          s: Option<CString>) -> IoResult<()> {
+          s: Option<&[u8]>) -> IoResult<()> {
     try!(write!(w, "  {:2}: {:2$} - ", idx, addr, HEX_WIDTH));
-    match s.as_ref().and_then(|c| c.as_str()) {
+    match s.and_then(|s| str::from_utf8(s).ok()) {
         Some(string) => try!(demangle(w, string)),
         None => try!(write!(w, "<unknown>")),
     }
diff --git a/src/libstd/sys/unix/fs.rs b/src/libstd/sys/unix/fs.rs
index b49ace8e2f8..1ad775517bb 100644
--- a/src/libstd/sys/unix/fs.rs
+++ b/src/libstd/sys/unix/fs.rs
@@ -12,7 +12,7 @@
 
 use prelude::v1::*;
 
-use c_str::{CString, ToCStr};
+use ffi::{self, CString};
 use io::{FilePermission, Write, UnstableFileStat, Open, FileAccess, FileMode};
 use io::{IoResult, FileStat, SeekStyle};
 use io::{Read, Truncate, SeekCur, SeekSet, ReadWrite, SeekEnd, Append};
@@ -150,6 +150,10 @@ impl Drop for FileDesc {
     }
 }
 
+fn cstr(path: &Path) -> CString {
+    CString::from_slice(path.as_vec())
+}
+
 pub fn open(path: &Path, fm: FileMode, fa: FileAccess) -> IoResult<FileDesc> {
     let flags = match fm {
         Open => 0,
@@ -165,7 +169,7 @@ pub fn open(path: &Path, fm: FileMode, fa: FileAccess) -> IoResult<FileDesc> {
                             libc::S_IRUSR | libc::S_IWUSR),
     };
 
-    let path = path.to_c_str();
+    let path = cstr(path);
     match retry(|| unsafe { libc::open(path.as_ptr(), flags, mode) }) {
         -1 => Err(super::last_error()),
         fd => Ok(FileDesc::new(fd, true)),
@@ -173,7 +177,7 @@ pub fn open(path: &Path, fm: FileMode, fa: FileAccess) -> IoResult<FileDesc> {
 }
 
 pub fn mkdir(p: &Path, mode: uint) -> IoResult<()> {
-    let p = p.to_c_str();
+    let p = cstr(p);
     mkerr_libc(unsafe { libc::mkdir(p.as_ptr(), mode as libc::mode_t) })
 }
 
@@ -182,7 +186,6 @@ pub fn readdir(p: &Path) -> IoResult<Vec<Path>> {
     use libc::{opendir, readdir_r, closedir};
 
     fn prune(root: &CString, dirs: Vec<Path>) -> Vec<Path> {
-        let root = unsafe { CString::new(root.as_ptr(), false) };
         let root = Path::new(root);
 
         dirs.into_iter().filter(|path| {
@@ -199,7 +202,7 @@ pub fn readdir(p: &Path) -> IoResult<Vec<Path>> {
     let mut buf = Vec::<u8>::with_capacity(size as uint);
     let ptr = buf.as_mut_ptr() as *mut dirent_t;
 
-    let p = p.to_c_str();
+    let p = CString::from_slice(p.as_vec());
     let dir_ptr = unsafe {opendir(p.as_ptr())};
 
     if dir_ptr as uint != 0 {
@@ -207,10 +210,9 @@ pub fn readdir(p: &Path) -> IoResult<Vec<Path>> {
         let mut entry_ptr = 0 as *mut dirent_t;
         while unsafe { readdir_r(dir_ptr, ptr, &mut entry_ptr) == 0 } {
             if entry_ptr.is_null() { break }
-            let cstr = unsafe {
-                CString::new(rust_list_dir_val(entry_ptr), false)
-            };
-            paths.push(Path::new(cstr));
+            paths.push(unsafe {
+                Path::new(ffi::c_str_to_bytes(&rust_list_dir_val(entry_ptr)))
+            });
         }
         assert_eq!(unsafe { closedir(dir_ptr) }, 0);
         Ok(prune(&p, paths))
@@ -220,39 +222,39 @@ pub fn readdir(p: &Path) -> IoResult<Vec<Path>> {
 }
 
 pub fn unlink(p: &Path) -> IoResult<()> {
-    let p = p.to_c_str();
+    let p = cstr(p);
     mkerr_libc(unsafe { libc::unlink(p.as_ptr()) })
 }
 
 pub fn rename(old: &Path, new: &Path) -> IoResult<()> {
-    let old = old.to_c_str();
-    let new = new.to_c_str();
+    let old = cstr(old);
+    let new = cstr(new);
     mkerr_libc(unsafe {
         libc::rename(old.as_ptr(), new.as_ptr())
     })
 }
 
 pub fn chmod(p: &Path, mode: uint) -> IoResult<()> {
-    let p = p.to_c_str();
+    let p = cstr(p);
     mkerr_libc(retry(|| unsafe {
         libc::chmod(p.as_ptr(), mode as libc::mode_t)
     }))
 }
 
 pub fn rmdir(p: &Path) -> IoResult<()> {
-    let p = p.to_c_str();
+    let p = cstr(p);
     mkerr_libc(unsafe { libc::rmdir(p.as_ptr()) })
 }
 
 pub fn chown(p: &Path, uid: int, gid: int) -> IoResult<()> {
-    let p = p.to_c_str();
+    let p = cstr(p);
     mkerr_libc(retry(|| unsafe {
         libc::chown(p.as_ptr(), uid as libc::uid_t, gid as libc::gid_t)
     }))
 }
 
 pub fn readlink(p: &Path) -> IoResult<Path> {
-    let c_path = p.to_c_str();
+    let c_path = cstr(p);
     let p = c_path.as_ptr();
     let mut len = unsafe { libc::pathconf(p as *mut _, libc::_PC_NAME_MAX) };
     if len == -1 {
@@ -273,14 +275,14 @@ pub fn readlink(p: &Path) -> IoResult<Path> {
 }
 
 pub fn symlink(src: &Path, dst: &Path) -> IoResult<()> {
-    let src = src.to_c_str();
-    let dst = dst.to_c_str();
+    let src = cstr(src);
+    let dst = cstr(dst);
     mkerr_libc(unsafe { libc::symlink(src.as_ptr(), dst.as_ptr()) })
 }
 
 pub fn link(src: &Path, dst: &Path) -> IoResult<()> {
-    let src = src.to_c_str();
-    let dst = dst.to_c_str();
+    let src = cstr(src);
+    let dst = cstr(dst);
     mkerr_libc(unsafe { libc::link(src.as_ptr(), dst.as_ptr()) })
 }
 
@@ -328,7 +330,7 @@ fn mkstat(stat: &libc::stat) -> FileStat {
 }
 
 pub fn stat(p: &Path) -> IoResult<FileStat> {
-    let p = p.to_c_str();
+    let p = cstr(p);
     let mut stat: libc::stat = unsafe { mem::zeroed() };
     match unsafe { libc::stat(p.as_ptr(), &mut stat) } {
         0 => Ok(mkstat(&stat)),
@@ -337,7 +339,7 @@ pub fn stat(p: &Path) -> IoResult<FileStat> {
 }
 
 pub fn lstat(p: &Path) -> IoResult<FileStat> {
-    let p = p.to_c_str();
+    let p = cstr(p);
     let mut stat: libc::stat = unsafe { mem::zeroed() };
     match unsafe { libc::lstat(p.as_ptr(), &mut stat) } {
         0 => Ok(mkstat(&stat)),
@@ -346,7 +348,7 @@ pub fn lstat(p: &Path) -> IoResult<FileStat> {
 }
 
 pub fn utime(p: &Path, atime: u64, mtime: u64) -> IoResult<()> {
-    let p = p.to_c_str();
+    let p = cstr(p);
     let buf = libc::utimbuf {
         actime: (atime / 1000) as libc::time_t,
         modtime: (mtime / 1000) as libc::time_t,
diff --git a/src/libstd/sys/unix/mod.rs b/src/libstd/sys/unix/mod.rs
index ea0d230e8b2..6a408aa60f0 100644
--- a/src/libstd/sys/unix/mod.rs
+++ b/src/libstd/sys/unix/mod.rs
@@ -15,12 +15,14 @@
 #![allow(unused_unsafe)]
 #![allow(unused_mut)]
 
-extern crate libc;
-
-use num;
-use num::{Int, SignedInt};
 use prelude::v1::*;
+
+use ffi;
 use io::{self, IoResult, IoError};
+use libc;
+use num::{Int, SignedInt};
+use num;
+use str;
 use sys_common::mkerr_libc;
 
 macro_rules! helper_init { (static $name:ident: Helper<$m:ty>) => (
@@ -78,11 +80,10 @@ extern "system" {
 }
 
 pub fn last_gai_error(s: libc::c_int) -> IoError {
-    use c_str::CString;
 
     let mut err = decode_error(s);
     err.detail = Some(unsafe {
-        CString::new(gai_strerror(s), false).as_str().unwrap().to_string()
+        str::from_utf8(ffi::c_str_to_bytes(&gai_strerror(s))).unwrap().to_string()
     });
     err
 }
diff --git a/src/libstd/sys/unix/os.rs b/src/libstd/sys/unix/os.rs
index 181b8fdd0f8..175c4e2e353 100644
--- a/src/libstd/sys/unix/os.rs
+++ b/src/libstd/sys/unix/os.rs
@@ -12,18 +12,18 @@
 
 use prelude::v1::*;
 
-use c_str::ToCStr;
 use error::{FromError, Error};
+use ffi::{self, CString};
 use fmt;
 use io::{IoError, IoResult};
 use libc::{self, c_int, c_char, c_void};
+use os::TMPBUF_SZ;
 use os;
 use path::{BytesContainer};
 use ptr;
+use str;
 use sys::fs::FileDesc;
 
-use os::TMPBUF_SZ;
-
 const BUF_BYTES : uint = 2048u;
 
 /// Returns the platform-specific value of errno
@@ -108,7 +108,8 @@ pub fn error_string(errno: i32) -> String {
             panic!("strerror_r failure");
         }
 
-        String::from_raw_buf(p as *const u8)
+        let p = p as *const _;
+        str::from_utf8(ffi::c_str_to_bytes(&p)).unwrap().to_string()
     }
 }
 
@@ -122,21 +123,17 @@ pub unsafe fn pipe() -> IoResult<(FileDesc, FileDesc)> {
 }
 
 pub fn getcwd() -> IoResult<Path> {
-    use c_str::CString;
-
     let mut buf = [0 as c_char; BUF_BYTES];
     unsafe {
         if libc::getcwd(buf.as_mut_ptr(), buf.len() as libc::size_t).is_null() {
             Err(IoError::last_error())
         } else {
-            Ok(Path::new(CString::new(buf.as_ptr(), false)))
+            Ok(Path::new(ffi::c_str_to_bytes(&buf.as_ptr())))
         }
     }
 }
 
 pub unsafe fn get_env_pairs() -> Vec<Vec<u8>> {
-    use c_str::CString;
-
     extern {
         fn rust_env_pairs() -> *const *const c_char;
     }
@@ -147,8 +144,7 @@ pub unsafe fn get_env_pairs() -> Vec<Vec<u8>> {
     }
     let mut result = Vec::new();
     while *environ != 0 as *const _ {
-        let env_pair =
-            CString::new(*environ, false).as_bytes_no_nul().to_vec();
+        let env_pair = ffi::c_str_to_bytes(&*environ).to_vec();
         result.push(env_pair);
         environ = environ.offset(1);
     }
@@ -234,14 +230,13 @@ pub fn load_self() -> Option<Vec<u8>> {
 }
 
 pub fn chdir(p: &Path) -> IoResult<()> {
-    p.with_c_str(|buf| {
-        unsafe {
-            match libc::chdir(buf) == (0 as c_int) {
-                true => Ok(()),
-                false => Err(IoError::last_error()),
-            }
+    let p = CString::from_slice(p.as_vec());
+    unsafe {
+        match libc::chdir(p.as_ptr()) == (0 as c_int) {
+            true => Ok(()),
+            false => Err(IoError::last_error()),
         }
-    })
+    }
 }
 
 pub fn page_size() -> uint {
diff --git a/src/libstd/sys/unix/pipe.rs b/src/libstd/sys/unix/pipe.rs
index 9063fbc2ba9..158a1ce2204 100644
--- a/src/libstd/sys/unix/pipe.rs
+++ b/src/libstd/sys/unix/pipe.rs
@@ -10,8 +10,8 @@
 
 use prelude::v1::*;
 
+use ffi::CString;
 use libc;
-use c_str::CString;
 use mem;
 use sync::{Arc, Mutex};
 use sync::atomic::{AtomicBool, Ordering};
@@ -48,7 +48,7 @@ fn addr_to_sockaddr_un(addr: &CString,
     }
     s.sun_family = libc::AF_UNIX as libc::sa_family_t;
     for (slot, value) in s.sun_path.iter_mut().zip(addr.iter()) {
-        *slot = value;
+        *slot = *value;
     }
 
     // count the null terminator
diff --git a/src/libstd/sys/unix/process.rs b/src/libstd/sys/unix/process.rs
index b73919fe2a2..5bc6b0c703b 100644
--- a/src/libstd/sys/unix/process.rs
+++ b/src/libstd/sys/unix/process.rs
@@ -11,8 +11,8 @@
 use prelude::v1::*;
 use self::Req::*;
 
-use c_str::{CString, ToCStr};
 use collections;
+use ffi::CString;
 use hash::Hash;
 use io::process::{ProcessExit, ExitStatus, ExitSignal};
 use io::{self, IoResult, IoError, EndOfFile};
@@ -101,7 +101,7 @@ impl Process {
 
                 // We may use this in the child, so perform allocations before the
                 // fork
-                let devnull = "/dev/null".to_c_str();
+                let devnull = b"/dev/null\0";
 
                 set_cloexec(output.fd());
 
@@ -204,7 +204,7 @@ impl Process {
                             } else {
                                 libc::O_RDWR
                             };
-                            libc::open(devnull.as_ptr(), flags, 0)
+                            libc::open(devnull.as_ptr() as *const _, flags, 0)
                         }
                         Some(obj) => {
                             let fd = obj.as_inner().fd();
diff --git a/src/libstd/sys/unix/timer.rs b/src/libstd/sys/unix/timer.rs
index 11f29232a92..62f3242a206 100644
--- a/src/libstd/sys/unix/timer.rs
+++ b/src/libstd/sys/unix/timer.rs
@@ -54,7 +54,7 @@ use libc;
 use mem;
 use os;
 use ptr;
-use sync::atomic::{mod, Ordering};
+use sync::atomic::{self, Ordering};
 use sync::mpsc::{channel, Sender, Receiver, TryRecvError};
 use sys::c;
 use sys::fs::FileDesc;
diff --git a/src/libstd/sys/windows/backtrace.rs b/src/libstd/sys/windows/backtrace.rs
index 319a458087b..4ccecfd1f5f 100644
--- a/src/libstd/sys/windows/backtrace.rs
+++ b/src/libstd/sys/windows/backtrace.rs
@@ -21,7 +21,8 @@
 /// copy of that function in my mingw install (maybe it was broken?). Instead,
 /// this takes the route of using StackWalk64 in order to walk the stack.
 
-use c_str::CString;
+use dynamic_lib::DynamicLibrary;
+use ffi;
 use intrinsics;
 use io::{IoResult, Writer};
 use libc;
@@ -30,10 +31,9 @@ use ops::Drop;
 use option::Option::{Some, None};
 use path::Path;
 use result::Result::{Ok, Err};
-use sync::{StaticMutex, MUTEX_INIT};
 use slice::SliceExt;
-use str::StrExt;
-use dynamic_lib::DynamicLibrary;
+use str::{self, StrExt};
+use sync::{StaticMutex, MUTEX_INIT};
 
 use sys_common::backtrace::*;
 
@@ -357,11 +357,11 @@ pub fn write(w: &mut Writer) -> IoResult<()> {
 
         if ret == libc::TRUE {
             try!(write!(w, " - "));
-            let cstr = unsafe { CString::new(info.Name.as_ptr(), false) };
-            let bytes = cstr.as_bytes();
-            match cstr.as_str() {
-                Some(s) => try!(demangle(w, s)),
-                None => try!(w.write(bytes[..bytes.len()-1])),
+            let ptr = info.Name.as_ptr() as *const libc::c_char;
+            let bytes = unsafe { ffi::c_str_to_bytes(&ptr) };
+            match str::from_utf8(bytes) {
+                Ok(s) => try!(demangle(w, s)),
+                Err(..) => try!(w.write(bytes[..bytes.len()-1])),
             }
         }
         try!(w.write(&['\n' as u8]));
diff --git a/src/libstd/sys/windows/c.rs b/src/libstd/sys/windows/c.rs
index 1ee57434fb9..945c2e8e7d1 100644
--- a/src/libstd/sys/windows/c.rs
+++ b/src/libstd/sys/windows/c.rs
@@ -133,7 +133,7 @@ pub mod compat {
     use intrinsics::{atomic_store_relaxed, transmute};
     use libc::types::os::arch::extra::{LPCWSTR, HMODULE, LPCSTR, LPVOID};
     use prelude::v1::*;
-    use c_str::ToCStr;
+    use ffi::CString;
 
     extern "system" {
         fn GetModuleHandleW(lpModuleName: LPCWSTR) -> HMODULE;
@@ -147,14 +147,13 @@ pub mod compat {
     unsafe fn store_func(ptr: *mut uint, module: &str, symbol: &str, fallback: uint) {
         let mut module: Vec<u16> = module.utf16_units().collect();
         module.push(0);
-        symbol.with_c_str(|symbol| {
-            let handle = GetModuleHandleW(module.as_ptr());
-            let func: uint = transmute(GetProcAddress(handle, symbol));
-            atomic_store_relaxed(ptr, if func == 0 {
-                fallback
-            } else {
-                func
-            })
+        let symbol = CString::from_slice(symbol.as_bytes());
+        let handle = GetModuleHandleW(module.as_ptr());
+        let func: uint = transmute(GetProcAddress(handle, symbol.as_ptr()));
+        atomic_store_relaxed(ptr, if func == 0 {
+            fallback
+        } else {
+            func
         })
     }
 
diff --git a/src/libstd/sys/windows/fs.rs b/src/libstd/sys/windows/fs.rs
index 9a942300656..f8c75335b35 100644
--- a/src/libstd/sys/windows/fs.rs
+++ b/src/libstd/sys/windows/fs.rs
@@ -13,7 +13,6 @@
 use alloc::arc::Arc;
 use libc::{self, c_int};
 
-use c_str::CString;
 use mem;
 use sys::os::fill_utf16_buf_and_decode;
 use path;
diff --git a/src/libstd/sys/windows/pipe.rs b/src/libstd/sys/windows/pipe.rs
index 9057515cad2..9996909f2f5 100644
--- a/src/libstd/sys/windows/pipe.rs
+++ b/src/libstd/sys/windows/pipe.rs
@@ -87,16 +87,21 @@
 use prelude::v1::*;
 
 use libc;
-use c_str::CString;
+use ffi::CString;
+use io::{self, IoError, IoResult};
 use mem;
 use ptr;
-use sync::{Arc, Mutex};
+use str;
 use sync::atomic::{AtomicBool, Ordering};
-use io::{self, IoError, IoResult};
+use sync::{Arc, Mutex};
 
 use sys_common::{self, eof};
 
-use super::{c, os, timer, to_utf16, decode_error_detailed};
+use super::{c, os, timer, decode_error_detailed};
+
+fn to_utf16(c: &CString) -> IoResult<Vec<u16>> {
+    super::to_utf16(str::from_utf8(c.as_bytes()).ok())
+}
 
 struct Event(libc::HANDLE);
 
@@ -270,7 +275,7 @@ impl UnixStream {
     }
 
     pub fn connect(addr: &CString, timeout: Option<u64>) -> IoResult<UnixStream> {
-        let addr = try!(to_utf16(addr.as_str()));
+        let addr = try!(to_utf16(addr));
         let start = timer::now();
         loop {
             match UnixStream::try_connect(addr.as_ptr()) {
@@ -571,7 +576,7 @@ impl UnixListener {
         // Although we technically don't need the pipe until much later, we
         // create the initial handle up front to test the validity of the name
         // and such.
-        let addr_v = try!(to_utf16(addr.as_str()));
+        let addr_v = try!(to_utf16(addr));
         let ret = unsafe { pipe(addr_v.as_ptr(), true) };
         if ret == libc::INVALID_HANDLE_VALUE {
             Err(super::last_error())
@@ -661,7 +666,7 @@ impl UnixAcceptor {
         // proceed in accepting new clients in the future
         if self.inner.closed.load(Ordering::SeqCst) { return Err(eof()) }
 
-        let name = try!(to_utf16(self.listener.name.as_str()));
+        let name = try!(to_utf16(&self.listener.name));
 
         // Once we've got a "server handle", we need to wait for a client to
         // connect. The ConnectNamedPipe function will block this thread until
@@ -753,7 +758,7 @@ impl UnixAcceptor {
 
 impl Clone for UnixAcceptor {
     fn clone(&self) -> UnixAcceptor {
-        let name = to_utf16(self.listener.name.as_str()).ok().unwrap();
+        let name = to_utf16(&self.listener.name).ok().unwrap();
         UnixAcceptor {
             inner: self.inner.clone(),
             event: Event::new(true, false).ok().unwrap(),
diff --git a/src/libstd/sys/windows/process.rs b/src/libstd/sys/windows/process.rs
index 81e8f974a12..9b3f2ca0373 100644
--- a/src/libstd/sys/windows/process.rs
+++ b/src/libstd/sys/windows/process.rs
@@ -10,27 +10,26 @@
 
 use prelude::v1::*;
 
+use collections;
+use ffi::CString;
+use hash::Hash;
+use io::fs::PathExtensions;
+use io::process::{ProcessExit, ExitStatus, ExitSignal};
+use io::{IoResult, IoError};
+use io;
 use libc::{pid_t, c_void, c_int};
 use libc;
-use c_str::{CString, ToCStr};
-use io;
 use mem;
 use os;
-use ptr;
-use io::process::{ProcessExit, ExitStatus, ExitSignal};
-use collections;
 use path::BytesContainer;
-use hash::Hash;
-use io::{IoResult, IoError};
-
+use ptr;
+use str;
+use sys::fs::FileDesc;
 use sys::fs;
 use sys::{self, retry, c, wouldblock, set_nonblocking, ms_to_timeval, timer};
-use sys::fs::FileDesc;
 use sys_common::helper_thread::Helper;
 use sys_common::{AsInner, mkerr_libc, timeout};
 
-use io::fs::PathExtensions;
-
 pub use sys_common::ProcessConfig;
 
 /// A value representing a child process.
@@ -142,10 +141,10 @@ impl Process {
                 // Split the value and test each path to see if the
                 // program exists.
                 for path in os::split_paths(v.container_as_bytes()).into_iter() {
-                    let path = path.join(cfg.program().as_bytes_no_nul())
+                    let path = path.join(cfg.program().as_bytes())
                                    .with_extension(os::consts::EXE_EXTENSION);
                     if path.exists() {
-                        return Some(path.to_c_str())
+                        return Some(CString::from_slice(path.as_vec()))
                     }
                 }
                 break
@@ -363,11 +362,11 @@ fn zeroed_process_information() -> libc::types::os::arch::extra::PROCESS_INFORMA
 
 fn make_command_line(prog: &CString, args: &[CString]) -> String {
     let mut cmd = String::new();
-    append_arg(&mut cmd, prog.as_str()
+    append_arg(&mut cmd, str::from_utf8(prog.as_bytes()).ok()
                              .expect("expected program name to be utf-8 encoded"));
     for arg in args.iter() {
         cmd.push(' ');
-        append_arg(&mut cmd, arg.as_str()
+        append_arg(&mut cmd, str::from_utf8(arg.as_bytes()).ok()
                                 .expect("expected argument to be utf-8 encoded"));
     }
     return cmd;
@@ -449,7 +448,7 @@ fn with_dirp<T, F>(d: Option<&CString>, cb: F) -> T where
 {
     match d {
       Some(dir) => {
-          let dir_str = dir.as_str()
+          let dir_str = str::from_utf8(dir.as_bytes()).ok()
                            .expect("expected workingdirectory to be utf-8 encoded");
           let mut dir_str: Vec<u16> = dir_str.utf16_units().collect();
           dir_str.push(0);