在一个通用函数中,是否有可能排除引用参数?

3

由于泛型类型参数 T 可以是任何类型,包括引用类型,我想知道是否有可能在泛型函数中排除引用类型,也就是能否像下面这样编写:

use std::ops::Deref;

fn foo<T: !Deref>(x: T) -> T {}

然而,这是不被允许的,并且在解析阶段就已经出现了错误。
我读到了关于optin_builtin_traits的内容,但它仅支持选择退出自动特性,因此也无法使用,因为Deref不是自动特性。
是否有可能实现这个?

2
为什么?如果你的函数可以操作任何类型,为什么它不应该操作一个引用? - Dan Hulme
@DanHulme 这只是一个示例案例;我也对如何一般性地排除其他特征感兴趣。 - ljedrz
1
@ljedrz,你遇到了哪些生命周期问题?即使有!Deref,也无法帮助你解决这些问题。对我来说,这种情况感觉像是一个XY问题。 - Wesley Wiser
4
虽然那个答案讲了很多关于T是一个引用的内容,但我认为如果T包含一个引用的话,情况也是一样的。比如:struct S<'a> { val: &'a str }。编译器必须确保任何类型为T的值在你返回的东西的生命周期内仍然有效。不过我可能有所错误... - Wesley Wiser
1
@WesleyWiser 噢,我以为你的意思是这不适用于包含引用的对象。我对这个结果很满意(即嵌套引用也不起作用)。 - ljedrz
显示剩余5条评论
1个回答

6

是的,您可以使用自动特性来实现这一点:

#![feature(auto_traits)]
#![feature(negative_impls)]

auto trait NotReference {}

impl<'a, T> !NotReference for &'a T {}
impl<'a, T> !NotReference for &'a mut T {}

fn no_references<T: NotReference>(_: T) {}

fn main() {
    no_references(42); // OK
    no_references(&42); // the trait bound `&{integer}: NotReference` is not satisfied
    no_references("hello"); // the trait bound `&str: NotReference` is not satisfied

    no_references(vec![1, 2, 3]); // OK

    let x = vec![1, 2, 3];
    no_references(x.iter()); // the trait bound `&{integer}: NotReference` is not satisfied in `std::slice::Iter<'_, {integer}>`
}

请注意,这也排除了以下情况:
  • 带有静态生命周期的引用,如“hello”调用所示
  • 任何包含引用的结构体,如iter()调用所示

我可能可以排除一些生命周期问题。

实际上,这就是'static限制的作用:
fn foo<T: 'static>(x: T) -> T {}

@ljedrz 噢... rustfmt 格式化时删除auto! 现在已经修复了,抱歉。 - Shepmaster
整洁;我曾认为 auto 是保留给现有的 std 特性,所以我试图用 Deref 想出一些解决方法。这个解决方法可能会很有用。 - ljedrz
当然可以。但是,这样做有什么意义呢?正如@Wesley Wiser所说,用户总是可以使用struct S<'a> { val: &'a str } - mcarton
3
请注意,自动派生 trait 的解决方案也会拒绝任何包含引用的类型,例如其他人提到的 S<'a>。与此同时,'static 约束将允许 &'static T。 自动派生 trait 解决方案还会拒绝包含引用(如其他人提到的 S<'a>)的任何类型。而 'static 约束可以接受 &'static T - kennytm
@Shepmaster:我能否不手动实现自动特性,而是为包装引用的结构体实现它? - Matthieu M.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接