我有以下代码片段(取消了生命周期try ):

pub struct NamedArgument<T>(pub(in crate) &'static str, pub(in crate) T);

pub struct LoggedArgument<T>(pub(in crate) &'static str, pub(in crate) T);

impl<T> NamedArgument<T> {
    pub fn log_with<T2, F>(self, log_level: log::Level, transform: F) -> LoggedArgument<T>
    where
        T2: Display,
        F: FnOnce(&T) -> T2,
    {
        log::log!(log_level, "{} = {}", self.0, transform(&self.1));

        LoggedArgument(self.0, self.1)
    }
}

我想这样称呼它:

fn foo(argument: NamedArgument<impl AsRef<str>>) {
    argument.log_with(log::Level::Info, |s| s.as_ref());
}

但此操作失败,并显示以下错误:

error: lifetime may not live long enough
  --> src/lib.rs:20:45
   |
20 |     argument.log_with(log::Level::Info, |s| s.as_ref());
   |                                          -- ^^^^^^^^^^ returning this value requires that `'1` must outlive `'2`
   |                                          ||
   |                                          |return type of closure is &'2 str
   |                                          has type `&'1 impl AsRef<str>`

我确实理解这个问题(至少我认为是这样),但我发现自己无法正确地指定生命周期. 本质上,我需要通过传递给闭包的参数指定返回的T2的生存期. 我try 了几十种生命周期规范的组合,包括为函数特征使用更高级别的特征界限.

如何指定使该系统正常运行所需的生命周期?

推荐答案

不幸的是,这在目前的铁 rust 是不可能的.您有两个 Select :

  1. 定义将&T转换为&T2的函数.这确实将其限制为返回引用的函数,因此您可以有两个函数,一个用于引用,另一个用于拥有的类型.这仍然不允许all种类型,但允许其中的绝对大多数类型.
impl<T> NamedArgument<T> {
    pub fn log_with<T2, F>(self, log_level: log::Level, transform: F) -> LoggedArgument<T>
    where
        T2: Display,
        F: FnOnce(&T) -> T2,
    {
        log::log!(log_level, "{} = {}", self.0, transform(&self.1));

        LoggedArgument(self.0, self.1)
    }
    
    pub fn log_with_reference<T2, F>(self, log_level: log::Level, transform: F) -> LoggedArgument<T>
    where
        T2: Display + ?Sized,
        F: FnOnce(&T) -> &T2,
    {
        log::log!(log_level, "{} = {}", self.0, transform(&self.1));

        LoggedArgument(self.0, self.1)
    }
}

fn foo(argument: NamedArgument<impl AsRef<str>>) {
    argument.log_with_reference(log::Level::Info, |s| s.as_ref());
}
  1. 定义变压器的自定义特性.使用GATalized特征,您可以涵盖所有可能的类型,并且还可以通过为所拥有的函数实现特征来方便大多数情况下的闭包语法,但对于其他所有情况,您必须使用不太方便的显式 struct 和特征实现:
pub trait Transformer<T> {
    type Output<'a>: Display
    where
        T: 'a;

    fn transform(self, arg: &T) -> Self::Output<'_>;
}

impl<T1, T2, F> Transformer<T1> for F
where
    F: FnOnce(&T1) -> T2,
    T2: Display,
{
    type Output<'a> = T2
    where
        T1: 'a;

    fn transform(self, arg: &T1) -> Self::Output<'_> {
        self(arg)
    }
}

impl<T> NamedArgument<T> {
    pub fn log_with<F>(self, log_level: log::Level, transform: F) -> LoggedArgument<T>
    where
        F: Transformer<T>,
    {
        log::log!(log_level, "{} = {}", self.0, transform.transform(&self.1));

        LoggedArgument(self.0, self.1)
    }
}

fn foo(argument: NamedArgument<impl AsRef<str>>) {
    argument.log_with(log::Level::Info, {
        struct MyTransformer<T>(std::marker::PhantomData<T>);

        impl<T: AsRef<str>> Transformer<T> for MyTransformer<T> {
            type Output<'a> = &'a str
            where
                T: 'a;

            fn transform(self, s: &T) -> Self::Output<'_> {
                s.as_ref()
            }
        }

        MyTransformer(std::marker::PhantomData)
    });
}

是的,那很长.这就是劣势.

Rust相关问答推荐

如何在Rust中获得不可辩驳的'if let'模式警告Mutex锁定?""

如何优化小型固定大小数组中的搜索?

如何在tauri—leptos应用程序中监听后端值的变化?""

我如何在Rust中使用传递依赖中的特征?

关于如何初始化弱 struct 字段的语法问题

有没有办法避免在While循环中多次borrow `*分支`

无法将记录器向下转换回原始 struct

如何循环遍历0..V.len()-1何时v可能为空?

无法定义名为&new&的关联函数,该函数的第一个参数不是self

为什么我们需要std::thread::scope,如果我们可以使用thread.join()在函数的生命周期内删除引用?

为什么铁 rust S的默认排序功能比我对小数组的 Select 排序稍微慢一些?

从未排序的链表中删除重复项的铁 rust 代码在ELSE分支的低级上做了什么?

实现 Deref 的 struct 可以返回对外部数据的引用吗?

Rust:为什么 &str 不使用 Into

我可以解构self 参数吗?

borrow 匹配手臂内部的可变

在 Rust 中,将可变引用传递给函数的机制是什么?

在 Rust 中如何将值推送到枚举 struct 内的 vec?

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

如何在宏中的多个参数上编写嵌套循环?