【问题标题】:Initialization of an inferred closure argument推断的闭包参数的初始化
【发布时间】:2025-12-23 05:10:16
【问题描述】:

我想找到一种方法来初始化 Rust 结构并将其作为唯一参数传递给 Fn(T) 闭包。但是,我想从闭包中推断结构(T)的类型。

因此调用call_with_props!(my_closure, id, 42); 应该扩展为my_closure(T {id: 42}); 或等效项,其中Tmy_closure 的唯一参数的类型。 (不过,我不一定要寻找基于宏的解决方案。)

struct A { id: i32 }
fn hello_1(props: A) { println!("hello {}", props.id); }

struct B { name: String }
fn hello_2(props: B) { println!("hi {}", props.name); }

macro_rules! call_with_props {
    // <------------------------------- What comes here?
}

fn main() {
    call_with_props!(hello_1, id, 42);
    // Should expand to: hello_1(A{id: 42}); 

    call_with_props!(hello_2, name, String::new("Bob"));
    // Should expand to: hello_2(B{name: String::new("Bob")}); 
}

【问题讨论】:

  • 您是否阅读过the book 或其他资源中的宏?您是否自己尝试过任何解决方案并遇到了绊脚石?
  • 你想要的不是很清楚(如果应该从参数类型中检测到类型,为什么要将“hello1”传递给宏?)。总的来说,您需要的只是定义一个特征并为 i32 和 String 添加实现。
  • @user4815162342 我已经阅读了包括 tlborm 在内的文档,但我仍然不清楚这是否可能。对我来说,绊脚石是 Rust 只允许命名结构初始化,所以 hello_1({id: 42}) 不会飞,即使编译器应该有足够的信息来确定需要做什么。
  • 宏在正常编译之前被评估,所以类型仍然未知。没有办法用宏“获取参数的类型”。
  • @Aplet123 谢谢。我尝试使用泛型来解决这个问题,但我无法推断出我可以这样使用的类型。我希望宏和模板的一些组合可以让我到达那里。

标签: rust


【解决方案1】:

如果您可以将#[derive(Default)] 添加到您的结构中,那么这可行:

// Create an instance of `T` and invoke a closure to initialize its fields.
// Allows creating a `T` without naming it, provided it is used in a context
// that accepts a concrete type. For example:
//
// hello_1(init(Default::default(), |v| v.id = 42));
fn init<T: Default>(mut v: T, f: impl FnOnce(&mut T)) -> T {
    f(&mut v);
    v
}

macro_rules! call_with_props {
    ($f: path, $($name: ident, $value: expr),+) => {{
        $f(init(Default::default(), |v| {
            $(v.$name = $value;)*
        }))
    }}
}

Playground

【讨论】:

  • 非常感谢,很棒的解决方案。 :)