-
Notifications
You must be signed in to change notification settings - Fork 13.7k
Description
@cmr, @huonw and I were talking in irc about how to name functions that allow you to convert from one type to another. For example, consider these str methods:
fn to_bytes(&str) -> ~[u8];
fn as_bytes<'a>(&'a str) -> &'a [u8]
These implement a common pattern, where in the to
case, we are copying the string into the new vec. In the as
case, we are making a no-copy cast from a string to a vector.
It'd be nice if we could standardize this pattern, and one way we could do this is to turn the as
operator into a trait a user can implement, like the Neg
trait. We can do this if we follow the pattern @nikomatsakis laid out on his blog. Here's a working example implementation:
use std::io;
trait Cast<LHS> {
fn cast(LHS) -> Self;
}
////
trait IntCast {
fn cast_int(&self) -> int;
}
impl<LHS: IntCast> Cast<LHS> for int {
fn cast(x: LHS) -> int { x.cast_int() }
}
impl IntCast for i8 {
fn cast_int(&self) -> int { *self as int }
}
impl IntCast for i16 {
fn cast_int(&self) -> int { *self as int }
}
////
trait StrCast {
fn cast_str(&self) -> ~str;
}
impl<LHS: StrCast> Cast<LHS> for ~str {
fn cast(x: LHS) -> ~str { x.cast_str() }
}
impl<'self> StrCast for &'self [u8] {
fn cast_str(&self) -> ~str { self.to_str() }
}
////
trait StrSliceCast<'self> {
fn cast_str_slice(&self) -> &'self str;
}
impl<'self, LHS: StrSliceCast<'self>> Cast<LHS> for &'self str {
fn cast(x: LHS) -> &'self str { x.cast_str_slice() }
}
impl<'self> StrSliceCast<'self> for &'self [u8] {
fn cast_str_slice(&self) -> &'self str {
unsafe {
assert!(std::str::is_utf8(*self));
let (ptr, len): (*u8, uint) = std::cast::transmute(*self);
std::cast::transmute((ptr, len + 1))
}
}
}
fn main() {
io::println(fmt!("%?", Cast::cast::<i8, int>(5_i8)));
io::println(fmt!("%?", Cast::cast::<i16, int>(5_i16)));
io::println(fmt!("%?", Cast::cast::<&[u8], ~str>(bytes!("hello world"))));
io::println(fmt!("%?", Cast::cast::<&[u8], &str>(bytes!("hello world"))));
}
While it's a bit wordy, it does work.
Unfortunately there's a third conversion option that I couldn't figure out how to fit into this paradigm, where we consume the input to produce the output:
fn to_option<T, U>(r: Result<T, U>) -> Option<T> {
match r {
Ok(x) => Some(x),
Err(_) => None,
}
}
Other places this would be useful is when we can cheaply transform ~str
into ~[u8]
, or consume all the elements from a HashMap
and move them into a ~[T]
. Perhaps a separate function would be best to capture this case. Or we do the reverse, and say Cast
consumes the input, but since going from one reference type to another is cheap, we optimize that specific case. I'm not sure.