How can a function be generic over a struct and uints in Rust, using methods defined for both?

huangapple go评论91阅读模式
英文:

How can a function be generic over a struct and uints in Rust, using methods defined for both?

问题

我有一个名为S的结构体定义

struct S {
  pub inner_state: [u8; N],
}

其中包含一些实现在设置边界之间旋转位的方法(即,我可以旋转最后5位,这是我需要做的)。

我还有一堆类似于的函数

pub fn compute_stuff(state: OtherStruct<S>) {
  do_bit_wise_operations!();
}

现在,由于结构体S实现了这些函数内部使用的所有方法,我想将这些函数泛化为T,其中T可以是无符号整数或结构体S

我尝试将函数签名更改为

pub fn compute_stuff<T>(state: OtherStruct<T>)
where T: std::ops::BitAnd<Output = T> + [...]
{
  do_bit_wise_operations!();
}

但我遇到了障碍。我使用了leading_zeros() 方法,该方法对结构体S存在,但我似乎找不到应该如何告诉编译器它的存在。另外,这样写会变得有点冗长,有没有一种方法可以为这个特定的T创建别名?
我还尝试创建一个名为BitOperations 的特性,并为S和无符号整数实现它,但我不太明白它如何帮助我解决这个问题。

英文:

So I have a struct S defined

struct S {
  pub inner_state: [u8; N],
}

with some methods that implement rotating a bit between set bounds (i.e. I can rotate bits for the last 5 bits, which I need to do.)
I also have a bunch of function like

pub fn compute_stuff(state: OtherStruct<S>) {
  do_bit_wise_operations!();
}

Now, since the struct S implements all the methods that are used inside these functions, I would like to make these function generics over T, where T can be either an uint or the struct S.

I tried changing the function signature to

pub fn compute_stuff<T>(state: OtherStruct<T>)
where T: std::ops::BitAnd<Output = T> + [...]
{
  do_bit_wise_operations!();
}

but I get to a road block. I use the leading_zeros() method that does exist for the struct S, but I can't seem to find how I should tell the compiler that it exists. Also, this get kinda long to write, is there a way to alias this specific T?
I also tried creating a trait BitOperations and impl it for S and the uints, but I don't really understand how it could help me solve this problem.

答案1

得分: 4

创建一个特性确实有效。首先,创建特性。由于您希望它增强现有特性的行为,您可以将其设置为BitAnd的子特性。

use std::ops::BitAnd;

pub trait BitOps: BitAnd<Output = Self> + Sized {
    fn leading_zeros(self) -> u32;
}

由于它使用了self的值,所以需要Sized

然后,您可以为您的类型实现特性。

// 为原始类型实现(可以使用宏来为多个原始类型执行此操作)
impl BitOps for i32 {
    fn leading_zeros(self) -> u32 {
        self.leading_zeros()
    }
}

impl BitAnd for S {
    type Output = Self;
    
    fn bitand(mut self, other: Self) -> Self {
        for (a, b) in self.inner_state.iter_mut().zip(other.inner_state) {
            *a = *a & b;
        }
        self
    }
}

// 为S实现特性
impl BitOps for S {
    fn leading_zeros(self) -> u32 {
        let mut zeros = 0;
        for byte in self.inner_state {
            if byte == 0 {
                zeros += 8;
            } else {
                zeros += byte.leading_zeros();
                break;
            }
        }
        zeros
    }
}

然后,您可以使用它。

pub fn compute_stuff<T>(state: T) where T: BitOps + Copy {
    let _leading_zeros = state.leading_zeros();
    let _bit_and = state & state;
}

这就是num-traits库的工作方式,但具有更多的特性和类型。它没有这么具体的特性,但PrimIntBitAnd的子特性,如果您想扩展功能,可能会有用。

英文:

Creating a trait does work. First, create the trait. Since you want it to augment the behavior of an existing trait, you can make it a subtrait of BitAnd.

use std::ops::BitAnd;

pub trait BitOps: BitAnd&lt;Output = Self&gt; + Sized {
    fn leading_zeros(self) -&gt; u32;
}

Sized is necessary since it takes self by value.

Then you can implement the trait for your types.

// Implemented for a primitive (a macro is useful to do this for many primitives)
impl BitOps for i32 {
    fn leading_zeros(self) -&gt; u32 {
        self.leading_zeros()
    }
}

impl BitAnd for S {
    type Output = Self;
    
    fn bitand(mut self, other: Self) -&gt; Self {
        for (a, b) in self.inner_state.iter_mut().zip(other.inner_state) {
            *a = *a &amp; b;
        }
        self
    }
}

// Implemented for S
impl BitOps for S {
    fn leading_zeros(self) -&gt; u32 {
        let mut zeros = 0;
        for byte in self.inner_state {
            if byte == 0 {
                zeros += 8;
            } else {
                zeros += byte.leading_zeros();
                break;
            }
        }
        zeros
    }
}

And then you can use it.

pub fn compute_stuff&lt;T&gt;(state: T) where T: BitOps + Copy {
    let _leading_zeros = state.leading_zeros();
    let _bit_and = state &amp; state;
}

This is how the num-traits library works, but with more traits and types. It doesn't have any traits that are this specific, but PrimInt is a subtrait of BitAnd and could be useful if you want to expand your functionality.

huangapple
  • 本文由 发表于 2023年6月29日 04:57:40
  • 转载请务必保留本文链接:https://go.coder-hub.com/76576661.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定