简体   繁体   English

如何指定可以取消引用到特征中的泛型

[英]How to specify generics that can be dereferenced into a trait

I have structs A and B , where B can be dereferenced into A .我有结构AB ,其中B可以取消引用到A

A implements GetValue with the function .get_value() , and therefore B also indirectly implements GetValue . A使用函数.get_value()实现GetValue ,因此B也间接实现GetValue

Now I'd like to write a function print_value that can accept &A and &B , so it can internally call .get_value() on them.现在我想写一个函数print_value可以接受&A&B ,所以它可以在内部调用.get_value() Technically this should be possible, because both of them can be dereferenced into &GetValue ;从技术上讲,这应该是可能的,因为它们都可以取消引用到&GetValue &A by dereferencing once, and &B by dereferencing twice. &A通过取消引用一次, &B通过取消引用两次。

The question is: How do I specify the generic of print_value to achieve this?问题是:如何指定print_value的泛型来实现这一点?


Here is my attempt:这是我的尝试:

use std::ops::Deref;

//### external library code ######################
struct A(i32);
struct B(A);

impl Deref for B {
    type Target = A;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

trait GetValue {
    fn get_value(&self) -> i32;
}

impl GetValue for A {
    fn get_value(&self) -> i32 {
        self.0
    }
}

//### my code ####################################
fn print_value<T: Deref>(val: T)
where
    <T as Deref>::Target: GetValue,
{
    println!("{}", val.get_value())
}

fn main() {
    let a = A(1);
    let b = B(A(2));

    println!("{}", a.get_value());
    println!("{}", b.get_value());

    print_value(&a);
    print_value(&b); // < fails with error "the trait bound `B: GetValue` is not satisfied"
}

The problem here is that <T as Deref>::Target: GetValue does not match &B , because &B would have to be dereferenced twice.这里的问题是<T as Deref>::Target: GetValue不匹配&B ,因为&B必须被取消引用两次。 The compiler automatically dereferences many times when a function is called, so is there a way to achieve the same through generics?调用函数时编译器会自动多次取消引用,那么有没有办法通过泛型来实现呢?


Note笔记

I only have power over the implementation of print_value .我只有执行print_value的权力。 I do not have the power over the other functions.我没有其他功能的权力。

A more real-life example would be:一个更真实的例子是:

  • get_value = get_error_source get_value = get_error_source
  • A = an error type A = 错误类型
  • B = Box<dyn A> B = Box<dyn A>
  • GetValue = std::error::Error GetValue = std::error::Error

I just wanted to keep it as abstract as possible to avoid confusion.我只是想让它尽可能抽象以避免混淆。 I'm still quite certain that this is not an XY problem.我仍然很确定这不是 XY 问题。

Instead of making a function have an overly complicated signature just to signify that it can take anything that you know to get_value of, you could do a blanket implementation.与其让一个函数有一个过于复杂的签名来表示它可以获取你知道的任何东西来get_value值,你可以做一个全面的实现。 This way, you automatically signify that something you can get_value of, well, you can get_value of.这样,您会自动表示您可以get_value of,嗯,您可以get_value of。

impl<T> GetValue for T
where
    T: Deref,
    <T as Deref>::Target: GetValue,
{
    fn get_value(&self) -> i32 {
        self.deref().get_value()
    }
}

Just adding that makes it work, see the playground .只需添加它即可使其工作, 请参阅操场

You just need to actually call deref on the object, then you would pass a B or an &A :您只需要在对象上实际调用deref ,然后您将传递B&A

use std::ops::Deref;

...

fn print_value<T: Deref>(val: T)
where
    <T as Deref>::Target: GetValue,
{
    println!("{}", val.deref().get_value())
}

fn main() {
    let a = A(1);
    let b = B(A(2));

    println!("{}", a.get_value());
    println!("{}", b.get_value());

    print_value(&a);
    print_value(b); 
}

Playground 操场

Outputs:输出:

1
2
1
2

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM