Skip to content

RFC: Consider turning as into a user-implementable Cast trait  #7080

@erickt

Description

@erickt

@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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions