[idea] foo !: Bar - type conversion operator

try this Lua code: print(1..0) and then print(1 .. 0)

Do you have a point, or are you just assigning arbitrary homework?

1 Like

it’s a small ambiguity in Lua. the former errors because 1. is a float.

we (rust) have a similar ambiguity with 1.max(0), 1..max(0) (this doesn’t do what you think it does), 1. .max(0), etc.

Swift has as! and as? operators.

For Rust it would make sense to map as? to TryFrom. However, it looks questionable with the try operator:

let bar = (foo as? Bar)?;

this is why I don’t believe in TryFrom as a trait/operator/operation.

it should be impl From<TryFrom<T>> for Option<U>, IMO. Optionally through a convenience trait so you don’t have to write it like this.

Interesting, probably stupid idea spitball:

name as? Type desugars to TryFrom::try_from(name)?: Type (where : is type ascription here, and ? is still error bubbling)

3 Likes

No, not stupid. That's interesting! and is worth investigation.

Not stupid at all. Actually, I believed it was how everyone expected the as? operator to work.

Others come up with as? idea as well (including me :slight_smile: ). The main problem IIUC is how to deprecate some of the current as behaviour (e.g. f32 -> u32 casts, pointer casts, etc.), so it indeed will become a sugar around From.

I believe the answer will need to be "Edition 2021" if we want to do that. The as operator isn't very loved anyways and probably doesn't deserve to be an operator (i.e. should be relegated to a trait...).

I mean we will need set of language intrinsics to handle conversions which are currently offloaded to as. (IIRC it’s currently impossible to convert f32 to u32 other than via as) BTW I am not sure if we indeed need full set of traits to describe all possible classes of conversions in the std. I think they can be first added to num-traits (or similar crate) to mature and implementations will use aforementioned intrinsics.

We don’t need more traits. From is all we need. If you think we need more, you’re doing it wrong.

Wrapping (256u16).into(): u8 == 0u8

256u16.into(): Option<u8> == None

These are simple IMO.

I’ve wanted something like this several times - it seems like some points in this thread have veered towards a fallible casting operator; my favorite mentioned syntax so far was as?.

Specifically, here’s one incarnation of something I’ve encountered many times (it usually involves enums, especially &str -> Enum, but there are plenty of other variants (excuse the pun) exhibiting a fallible cast/conversion like operation):

#[repr(usize)]
#[derive(Debug)]
pub enum Normal {
    North = 0,
    South = 1,
    West = 2,
    East = 3,
    Up = 4,
    Down = 5,
}

impl Normal {
    // I want to implement a trait so I and my clients
    // don't have to memorize another random function name
    pub fn from_idx(idx: usize) -> Option<Self> {
        use self::Normal::*;
        match idx {
            0 => Some(North),
            1 => Some(South),
            2 => Some(West),
            3 => Some(East),
            4 => Some(Up),
            5 => Some(Down),
            _ => None,
        }
    }
}

fn main() -> Result<(), Error> {
  let i = 3;
  // i want to write this for the fallible cast/conversion
  let n = i as? Normal;
  // always safe
  let i2 = n as usize;
  assert_eq!(i, i2);
}

Yes, I can write a custom method, e.g. from_idx. But having a fallible cast syntax like above, just appears much more elegant, succinct, obvious, and universally memorable to me.

1 Like

We don’t need new traits to have a trait based conversion operator, but we need new intrinsics if we want to deprecate the as operator because we have to be able to write implementations of From and TryFrom without using as.

For instance the current implementation for numeric conversion in the std::convert::From trait is :

    impl From<$Small> for $Large {
        #[inline]
        fn from(small: $Small) -> $Large {
            small as $Large
        }
    }

We need an intrinsic to replace the as in this implementation.

Furthermore, we need to define some trait implementation too, that are not defined yet by neither From nor TryFrom like conversion from f64 to u16.

if plat == big { mem::transmute([0, $small]): $Large } else { mem::transmute

etc

as for f64 etc those can be done mathematically and the compiler can optimize them later.

in any case, I don’t like TryFrom, as it does mean we’ll need two separate conversion operators. I also prefer being able to return different types for different conditions.

For example:

Wrapping (256u16) !: u8 == 0u8
256u16 !: Option<u8> == None (or Result<u8, IntFail>)
256u16 !: Result<u8, !> == panic at runtime

Maybe the following syntax sugar would make more sense?

let a = String::"literal"; // String::from("literal")
let b = String::10;        // String::from(10)
let c = String::(value);   // String::from(value)

And going further:

let x = u16::256;
let y = bool::true;

An intrinsic would be cleaner than an architecture and optimization dependent hack.

It would be even more confusing to me since :: currently has a completely different purpose.

3 Likes

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.