Repository navigation
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.
Activity
erickt commentedon Jun 12, 2013
You can find a version that's using moves here: https://gist.github.com/erickt/5762933, but unfortunately I'm getting this llvm bug
I expect it's related to #4759.
Kimundi commentedon Jun 12, 2013
Note that the reason for a build-in
asis mainly casts in constexprs. Also, it's used for casting to a Trait object. Replacingaswith methods, or turningasinto sugar for them is a nice idea, but there needs to be solution for sth like this:However, seeing how
asis only used for arithmetic casts otherwise,It might be sensible to replace the constexpr-use case with a
static_cast!()syntax extension:which would be hard-coded to expand to a literal of the right type.
In fact, such a extension would nicely subsume the current
bytes!()-se.emberian commentedon Jun 12, 2013
Note that I've been thinking about CTFE and how it can be used with static items. I think @bblum's effect proposal could easily express this: only functions/methods without certain effects are valid for consideration for CTFE. This would fit in nicely with
#[static_assert](for example, I want to assert that two&'static[&'static str]are the same size , but currently cannot because.len()(a method call) is not currently allowable). Combine that with anastrait and perhaps it could be allow in a static context? Although I'm not sure how well that'd work out in practice: sometimes you want the conversion to have effects, or need effects to actually do the cast. So maybestatic_cast!()isn't a bad idea (or just special-casing the numeric casts).I didn't consider how this would interact with trait objects and I don't know enough about them to evaluate its effect on them.
erickt commentedon Jun 12, 2013
@Kimundi: curse you, constant expressions! Bane of my existence. We could tone this down, and provide these traits to standardize how we transform from one type to another, and keep
asaround for the constant expressions.Another option is to copy the
bytes!syntax extension, and implement enough compile-time-typecasts to cover cases like you mentioned:Either way, it's not that common to use these static casts, at least in rust proper. So I wouldn't be too upset if it was a little uglier to use if it allowed us to take better advantage of the
asoperator, or even reclaim it for the user. Here's all the uses I could find:emberian commentedon Jun 17, 2013
@erickt I think with the effect system we could even have the trait be CTFE for the current static uses. It could be hardcoded in the interim, just to keep things pretty. @bblum?
emberian commentedon Jun 17, 2013
Heh, I already left a comment to that end, nevermind 😊
bblum commentedon Jun 17, 2013
A constexpr effect would be tractable, I think. Might be an issue with asserts -- if you wanted to write asserts in your function but have it be constexpr anyway, either you would have to pick one, or the compiler would be smart enough to turn the assert into a compile fail (which seems both possible and pretty leet, but difficult). The latter strikes me as a far-future feature.
Also keep in mind effects won't be in 1.0, so it would have to be backwards-compatible.
emberian commentedon Jun 20, 2013
@erickt I think hardcoding in for the existing cases would be fine.
emberian commentedon Sep 5, 2013
Nominating for well-defined: this is a language feature.
thestinger commentedon Sep 5, 2013
I'm against doing this because I don't think one type of conversion should be elevated above the others at a language level. For example, you can downcast to a smaller integer type by truncating, or you can clamp to the max value.
emberian commentedon Sep 5, 2013
That's a good argument. Having multiple types of conversions makes an overloadable
asless straightforward and useful.thestinger commentedon Sep 5, 2013
I would be happiest if we lived in a world where Rust had CTFE, and we didn't need
asin the language at all. A special case like that feels like a language wart to me.catamorphism commentedon Sep 5, 2013
Declining for milestone. We decided this is part of the larger story about constant evaluation, and there are already other bugs open on that.
nikomatsakis commentedon Sep 5, 2013
My two cents: The reason we have
asat all is to distinguishcasts that can be done in constants. If we were going to use a
trait, let's just make it a normal trait (or multiple, as
strcat suggests). This would also solve the problem of the
precedence of
as. Therefore this really comes down todeciding on our constant strategy.
emberian commentedon Sep 5, 2013
@catamorphism which bugs?
pnkfelix commentedon Sep 9, 2013
@cmr I think #5551 is the relevant metabug here.
Merge rust-lang#7080
Merge pull request #7080 from ytmimi/cfg_release_channel_on_diff_check