"Implicit" Into when constructing a struct?

Given the following snippet

//Some_other_file.rs
pub struct Foo(f32, f32, f32, f32);

impl Into<Foo> for f32 {
    fn into(self) -> Foo {
        Foo(self, self, self, self)
    }
}

impl Into<Foo> for (f32, f32, f32, f32) {
    fn into(self) -> Foo {
        Foo(self.0, self.1, self.2, self.3)
    }
}

pub struct Bar {
    foo: Foo,
}

impl Bar {
    pub fn new(val: impl Into<Foo>) -> Self {
        Self { foo: val.into() }
    }
}

//main.rs
fn main() {
    let a = Bar {
        foo: (0.0, 0.0, 0.0, 0.0).into(),
    };

    let b = Bar { foo: 0.0.into() };

    let c = Bar::new(0.0);
    let d = Bar::new((0.0, 0.0, 0.0, 0.0));
}

Can the .into() of a and b be hidden away from being (explicitly) called when creating a struct, the same way c and d do?

It's very intentional that struct initialization syntax just moves the values to the correct place, and doesn't run any code.

If one wants to run code, then making a function is the way to go.

3 Likes

Hadn't thought abut that. Makes sense, thanks!

A shame that it cannot happen though, would make it slightly less verbose to read. (Which well, is both a good and bad thing depending on how you look at it.)

Yeah, exactly. There are a bunch of places where it wouldn't be an issue, but things like Foo { a: "hello" } actually allocating-and-copying the &str into a String is something that people generally don't want to happen.

Maybe we'll one day get some sort of sufficiently-restricted into-like thing that people wouldn't be worried about it applying even in strict literals, but right now it does way too much complicated stuff, even just in std impls.

I'd like some sort of .into() esque syntax to exist for structs, where you have to explicitly opt in to the conversion, but it is hidden away from creating new ones. Maybe like serde's field attributes for renaming and whatnot.

Its not a perfect solution, but IMO it could be a compromise if you ignore whatever issues I'm glossing over.

It's not even a good one. It's even worse than just always doing the conversion implicitly, because now you have to look up the definition of the struct in order to know whether it behaves funnily.

Writing .into() shouldn't be considered a burden. Seriously, it's 7 characters, and it saves the programmer from having to look up stuff constantly (as well as preventing syntactic bloat in the language). We read code a lot more than we write it, so optimizing for writing fast at the expense of providing important context while reading is an inappropriate trade-off.

1 Like

That's the approach C++ took and now people have issues where an implicitly called copy/conversion constructor causes loads of hidden code to be run.

Often accepting an impl Into<Foo> argument is the least obtrusive way of letting the caller have implicit conversions while still keeping them visible.

2 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.