作为更大系统的一部分,我有一组可以回调的强类型函数.尽管由于该系统的动态特性,提供给这些函数的实际参数存储为Rc<dyn Any>.为了使API保持尽可能强的类型,函数参数都是运行时可转换的From<&'a dyn Any>.这些函数参数的生存期限制为'a(它们可以被视为视图,或者只是公开对dyn Any的底层实例的引用).

当用户注册一个新函数时,会自动为它们添加一个执行从Rc<dyn Any>到强类型参数转换的闭包.代码可以简化为以下内容:

use std::{any::Any, rc::Rc};

struct View<'a, T> {
    view: &'a [T],
}

struct Foo {
    bar: String,
}

impl<'a, T: 'static> From<&'a dyn Any> for View<'a, T> {
    fn from(value: &'a dyn Any) -> Self {
        Self {
            view: value.downcast_ref::<Vec<T>>().unwrap(),
        }
    }
}

impl<'a> From<&'a dyn Any> for &'a Foo {
    fn from(value: &'a dyn Any) -> Self {
        value.downcast_ref().unwrap()
    }
}

fn print_content(view: View<i32>, foo: &Foo) {
    println!("view: {:?}", view.view);
    println!("foo: {}", foo.bar);
}

fn create_function() -> Box<dyn Fn(&[Rc<dyn Any>])> {
    Box::new(|args: &[Rc<dyn Any>]| print_content(args[0].as_ref().into(), args[1].as_ref().into()))
}

fn main() {
    let a = Rc::new(vec![1, 2, 3]);
    let b = Rc::new(Foo {
        bar: "hello".into(),
    });

    let args = vec![a as Rc<dyn Any>, b];

    let f: Box<dyn Fn(&[Rc<dyn Any>])> = create_function();
    f(&args);
}

到目前一切尚好.将print_content()方法显式提供给编译器后,生存期得到了正确解析,代码编译和运行都很好,打印:

view: [1, 2, 3]
foo: hello

当我试图创建一个以该函数为参数的泛型方法时,事情就变得一团糟:

fn create_function_with<'a, Function, A, B>(f: Function) -> Box<dyn Fn(&'a [Rc<dyn Any>])>
where
    Function: 'static + Fn(A, B),
    A: From<&'a dyn Any>,
    B: From<&'a dyn Any>,
{
    Box::new(move |args| f(args[0].as_ref().into(), args[1].as_ref().into()))
}

fn main() {
    let a = Rc::new(vec![1, 2, 3]);
    let b = Rc::new(Foo {
        bar: "hello".into(),
    });

    let args = vec![a as Rc<dyn Any>, b];

    let f: Box<dyn Fn(&[Rc<dyn Any>])> = create_function_with(print_content);
    f(&args);
}

编译错误为:

error[E0308]: mismatched types
  --> src/main.rs:54:42
   |
54 |     let f: Box<dyn Fn(&[Rc<dyn Any>])> = create_function_with(print_content);
   |                                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other
   |
   = note: expected trait object `dyn for<'a> Fn(&'a [Rc<dyn Any>])`
              found trait object `dyn Fn(&[Rc<dyn Any>])`

暗示create_function_with()的生命周期 限制并不完全返回dyn Fn(&[Rc<dyn Any>]) that works independently of the lifetime of &amp;[RC]`.

我已经try 了上面的许多变体,包括使用更高等级的特征界限.我不知道我是否已经达到了我对Rust知识的限制(可能)或语言本身的限制(不太可能).

同时,我已经设法使用宏解决了这个问题(有效地返回到转换闭包中内联了函数名称的初始工作版本).

这里是否遗漏了一些高级生命周期约束/定义?

推荐答案

请注意,返回值create_function的实际类型为

    let f : Box<dyn for <'a> Fn(&'a [Rc<dyn Any>])> = create_function();

粗略地说,由于生存期的省略,返回类型中没有显式边界的引用会被最近的绑定器泛化.这在Rust中经常发生,它让你可以写fn print_content(view: View<i32>, foo: &Foo)而不是fn print_content<'a>(view: View<'a, i32>, foo: &'b Foo)这样的东西.

这是引用any个生存期的函数的类型.

这种强类型函数的边界是什么样子的?嗯,你可以试试这个:

fn create_function_with<A, B, Function>(f: Function) -> Box<dyn for<'a> Fn(&'a [Rc<dyn Any>])>
where
    Function: 'static + for <'x, 'y> Fn(View<'x, A>, &'y B)

但这对您并不是特别有用,因为您有多种不同的视图类型(可能比这两种类型更多),并且它们可以出现在不同的参数位置.因此,您必须进一步抽象-泛型类型AB应该是类型形成器,并且您应该能够用From约束形成的类型:

fn create_function_with<A, B, Function>(f: Function)
where
    Function: 'static + for <'x, 'y> Fn(A<'x>, B<'y>)
           where A<'x> : From<&'x dyn Any>, B<'x> : From<&'y dyn Any>

请注意,这是假设的语法,它甚至不进行解析.这里的A和B是更高级的类型,不受Rust支持.你可以模拟这些,但它很难使用,而且不利于构图.在这一点上,我建议使用宏观解决方案,但也可以这样做:

trait ViewLike
{
    type ViewT<'x>;
    fn from_dyn<'x>(value: &'x dyn Any) -> Self::ViewT<'x>;
}

impl<T : 'static> ViewLike for View<'static, T> {
    type ViewT<'x> = View<'x, T>;
    
    fn from_dyn<'x>(value: &'x dyn Any) -> View<'x, T>
    {
        View {
            view: value.downcast_ref::<Vec<T>>().unwrap(),
        }
    }
}

impl<T : 'static> ViewLike for &'static T {
    type ViewT<'x> = &'x T;
    
    fn from_dyn<'x>(value: &'x dyn Any) -> &'x T
    {
        value.downcast_ref::<T>().unwrap()
    }
}

请注意,这里ViewLikeSelf类型并不重要,我们从不将其用作值的类型.这只是一种标签类型.我们需要参考<Something as ViewLike>::ViewT<'x>来获得真正的值类型.我们在这里失go 了很多类型推断.

fn create_function_with<Va, Vb, Function>(f: Function) -> Box<dyn for<'a> Fn(&'a [Rc<dyn Any>])>
where
    Function: 'static + for <'x, 'y> Fn(<Va as ViewLike>::ViewT<'x>, <Vb as ViewLike>::ViewT<'y>),
    Va : ViewLike,
    Vb : ViewLike,
{
    Box::new(move |args| f(
        <Va as ViewLike>::from_dyn(args[0].as_ref()),
        <Vb as ViewLike>::from_dyn(args[1].as_ref())))
}

fn main() {
    ...
    let f = create_function_with::<View<i32>, &Foo, _>(print_content);
    f(&args);
}

Rust相关问答推荐

空字符串转换为Box字符串时是否分配?<>

有没有更好的方法从HashMap的条目初始化 struct ?

带扫描的铁 rust 使用滤镜

使用铁 rust S还原对多个数组执行顺序kronecker积

一种随机局部搜索算法的基准(分数)

不能在一个代码分支中具有不变的自身borrow ,而在另一个代码分支中具有可变的self borrow

为什么`AlternateScreen`在读取输入键时需要按Enter键?

是否提供Bundle 在可执行文件中的warp中的静态文件?

如何将带有嵌套borrow /NLL 的 Rust 代码提取到函数中

如何执行数组文字的编译时串联?

在 Rust 中,是否可以定义一个需要实现类型的构造函数的对象安全特征?

为什么 Rust 需要可变引用的显式生命周期而不是常规引用?

如何以与平台无关的方式将OsString转换为utf-8编码的字符串?

仅当函数写为闭包时才会出现生命周期错误

预期的整数,找到 `&{integer}`

有没有办法在 Rust 中对 BigInt 进行正确的位移?

如何从 Rust 中不同类型的多个部分加入 Path?

Rust 中的运行时插件

在 Rust 中组合特征的不同方法是否等效?

缓存自引用函数导致 Rust