Skip to content

Commit

Permalink
Rollup merge of rust-lang#35755 - SimonSapin:char_convert, r=alexcric…
Browse files Browse the repository at this point in the history
…hton

Implement std::convert traits for char

This is motivated by avoiding the `as` operator, which sometimes silently truncates, and instead use conversions that are explicitly lossless and infallible.

I’m less certain that `From<u8> for char` should be implemented: while it matches an existing behavior of `as`, it’s not necessarily the right thing to use for non-ASCII bytes. It effectively decodes bytes as ISO/IEC 8859-1 (since Unicode designed its first 256 code points to be compatible with that encoding), but that is not apparent in the API name.
  • Loading branch information
eddyb committed Aug 23, 2016
2 parents d86838e + 82678c5 commit dadd8ee
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 6 deletions.
43 changes: 37 additions & 6 deletions src/libcore/char.rs
Expand Up @@ -18,6 +18,7 @@
use prelude::v1::*;

use char_private::is_printable;
use convert::TryFrom;
use mem::transmute;

// UTF-8 ranges and tags for encoding characters
Expand Down Expand Up @@ -123,12 +124,7 @@ pub const MAX: char = '\u{10ffff}';
#[inline]
#[stable(feature = "rust1", since = "1.0.0")]
pub fn from_u32(i: u32) -> Option<char> {
// catch out-of-bounds and surrogates
if (i > MAX as u32) || (i >= 0xD800 && i <= 0xDFFF) {
None
} else {
Some(unsafe { from_u32_unchecked(i) })
}
char::try_from(i).ok()
}

/// Converts a `u32` to a `char`, ignoring validity.
Expand Down Expand Up @@ -176,6 +172,41 @@ pub unsafe fn from_u32_unchecked(i: u32) -> char {
transmute(i)
}

#[stable(feature = "char_convert", since = "1.12.0")]
impl From<char> for u32 {
#[inline]
fn from(c: char) -> Self {
c as u32
}
}

#[stable(feature = "char_convert", since = "1.12.0")]
impl From<u8> for char {
#[inline]
fn from(i: u8) -> Self {
i as char
}
}

#[unstable(feature = "try_from", issue = "33417")]
impl TryFrom<u32> for char {
type Err = CharTryFromError;

#[inline]
fn try_from(i: u32) -> Result<Self, Self::Err> {
if (i > MAX as u32) || (i >= 0xD800 && i <= 0xDFFF) {
Err(CharTryFromError(()))
} else {
Ok(unsafe { from_u32_unchecked(i) })
}
}
}

/// The error type returned when a conversion from u32 to char fails.
#[unstable(feature = "try_from", issue = "33417")]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct CharTryFromError(());

/// Converts a digit in the given radix to a `char`.
///
/// A 'radix' here is sometimes also called a 'base'. A radix of two
Expand Down
18 changes: 18 additions & 0 deletions src/libcoretest/char.rs
Expand Up @@ -9,6 +9,24 @@
// except according to those terms.

use std::char;
use std::convert::TryFrom;

#[test]
fn test_convert() {
assert_eq!(u32::from('a'), 0x61);
assert_eq!(char::from(b'\0'), '\0');
assert_eq!(char::from(b'a'), 'a');
assert_eq!(char::from(b'\xFF'), '\u{FF}');
assert_eq!(char::try_from(0_u32), Ok('\0'));
assert_eq!(char::try_from(0x61_u32), Ok('a'));
assert_eq!(char::try_from(0xD7FF_u32), Ok('\u{D7FF}'));
assert!(char::try_from(0xD800_u32).is_err());
assert!(char::try_from(0xDFFF_u32).is_err());
assert_eq!(char::try_from(0xE000_u32), Ok('\u{E000}'));
assert_eq!(char::try_from(0x10FFFF_u32), Ok('\u{10FFFF}'));
assert!(char::try_from(0x110000_u32).is_err());
assert!(char::try_from(0xFFFF_FFFF_u32).is_err());
}

#[test]
fn test_is_lowercase() {
Expand Down
2 changes: 2 additions & 0 deletions src/librustc_unicode/char.rs
Expand Up @@ -39,6 +39,8 @@ pub use core::char::{MAX, from_digit, from_u32, from_u32_unchecked};
pub use core::char::{EncodeUtf16, EncodeUtf8, EscapeDebug, EscapeDefault, EscapeUnicode};

// unstable reexports
#[unstable(feature = "try_from", issue = "33417")]
pub use core::char::CharTryFromError;
#[unstable(feature = "decode_utf8", issue = "33906")]
pub use core::char::{DecodeUtf8, decode_utf8};
#[unstable(feature = "unicode", issue = "27783")]
Expand Down
1 change: 1 addition & 0 deletions src/librustc_unicode/lib.rs
Expand Up @@ -37,6 +37,7 @@
#![feature(decode_utf8)]
#![feature(lang_items)]
#![feature(staged_api)]
#![feature(try_from)]
#![feature(unicode)]

mod tables;
Expand Down

0 comments on commit dadd8ee

Please sign in to comment.