Is it possible to not have to name struct type in function

Hello,
I wanted to not have to repeat the type of a temporary struct that I pass to a function.

let s = S;

// this works but is unnecessary verbose
s.foo(DataForS{x: 1, y: 2});

// this doesn't compiles
s.foo({x: 1, y: 2});

The syntax I used is what you can do in C++. AFAIK it's not possible at all in Rust to use/emulate it, but I wanted to be sure.

My real use-case is that I am creating a trait, and I want to be able to pass custom data to one of the functions of that trait. The type of that custom data will change for every implementation of the trait and will only ever be used for that function. I'm happy with everything but the verbosity of the call site, since it's redundant to have to specify the type of the data passed to this function (that information is already visible with the type of the object implementing the trait).

playground

No, that's not possible.

1 Like

Well, it's possible... kinda. Check this out:

macro_rules! auto_arg {
    ($fn:ident($ty:ident)) => {
        macro_rules! $fn {
            ($arg:tt) => {
                $fn($ty $arg)
            }
        }
    }
}

struct Data{x: i32, y: i32}
fn foo(Data{x, y}: Data) {
    println!("Got Data[x = {}, y = {}]", x, y);
}
auto_arg!(foo(Data));

fn main() {
    foo!({x: 1, y: 2});
}

I wasn't able to get this working for methods, but for free functions - that's the idea: to call the function through the macro, which, in turn, can be auto-generated by another macro. This is not very pretty, but if you're interested, that's what I'd dug into.

Playground

3 Likes

There are proposals that get what you're going for here -- keyword arguments via (unnamed) struct argument -- but these are far out.

It might be possible someday, but it's not currently, and won't be for some time (without macro indirection).

Thanks for the idea. I will not use it, but I keep it for later.

It was what I was expecting. Thanks for the details.

In nightly (with const_generics), there is a way to express the "structure" of a struct within the type / trait system, making it possible to define a genericity over "structs with (at least) the same fields", at which point one can create a dummy struct with the given fields. And all that can be wrapped using macros:

#[derive(Debug)]
struct Person {
    name: String,
    age: u8,
}
/// Imagine this is generated by a derive macro:
const _: () = {
    impl<T> IntoStruct<Person> for T
    where
        T : Sized
            + TakeField<"name", T = String>
            + TakeField<"age", T = u8>
    {
        fn into_struct (mut self: Self) -> Person
        {
            Person {
                name: TakeField::<"name">::take(&mut self),
                age: TakeField::<"age">::take(&mut self),
            }
        }
    }
};

#[derive(Debug)]
struct Animal {
    name: String,
    age: u8,
}
/// Ditto
impl<T> IntoStruct<Animal> for T where T : ...

fn foo_person (p: Person)
{
    dbg!(p);
}

fn foo_animal (p: Animal)
{
    dbg!(p);
}

fn main ()
{
    foo_person(S! {
        name: "John!".into(),
        age: 42,
    });
    foo_animal(S! {
        name: "Laika".into(),
        age: 6,
    });
}

FWIW, it seems to be possible to express this const _: &'static str type-level genericity over the fields names without const_generics, by expressing strings directly at the type level, which the ::structural crate seems to achieve.

1 Like

See also frunk's LabelledGeneric. (Those four blog posts are a great explainer on advanced type-level shenanigans.)

2 Likes

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