如何返回一个新创建的结构体作为参考? [英] How to return a newly created struct as a reference?

查看:46
本文介绍了如何返回一个新创建的结构体作为参考?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

作为学习 Rust 的练习,我决定实现一个位向量库,灵感来自 std::vec::Vec 提供哪些方法.

As an exercise to learn Rust I decided to implement a Bit Vector library, with inspiration from std::vec::Vec for which methods to provide.

我有以下代码:

extern crate num;

use std::cmp::Eq;
use std::ops::{BitAnd,BitOrAssign,Index,Shl};
use num::{One,Zero,Unsigned,NumCast};

pub trait BitStorage: Sized + 
    BitAnd<Self, Output = Self> + 
    BitOrAssign<Self> + 
    Shl<Self, Output = Self> + 
    Eq + Zero + One + Unsigned + NumCast + Copy {}

impl<S> BitStorage for S where S: Sized + 
    BitAnd<S, Output = S> + 
    BitOrAssign<S> + 
    Shl<S, Output = S> + 
    Eq + Zero + One + Unsigned + NumCast + Copy {}

pub struct BitVector<S: BitStorage> {
    data: Vec<S>,
    capacity: usize,
    storage_size: usize
}

impl<S: BitStorage> BitVector<S> {
    pub fn with_capacity(capacity: usize) -> BitVector<S> {
        let storage_size = std::mem::size_of::<S>() * 8;
        let len = (capacity / storage_size) + 1;
        BitVector { 
            data: vec![S::zero(); len],
            capacity: capacity,
            storage_size: storage_size
        }
    }

    pub fn get(&self, index: usize) -> Option<bool> {
        match self.index_in_bounds(index) {
            true => Some(self.get_unchecked(index)),
            false => None
        }
    }

    pub fn set(&mut self, index: usize, value: bool) {
        self.panic_index_bounds(index);
        let (data_index, remainder) = self.compute_data_index_and_remainder(index);
        let value = if value { S::one() } else { S::zero() };
        self.data[data_index] |= value << remainder;
    }

    pub fn capacity(&self) -> usize {
        self.capacity
    }

    pub fn split_at(&self, index: usize) -> (&BitVector<S>, &BitVector<S>) {
        self.panic_index_not_on_storage_bound(index);
        let data_index = self.compute_data_index(index);
        let (capacity_left, capacity_right) = self.compute_capacities(index);
        let (data_left, data_right) = self.data.split_at(data_index);

        let left = BitVector {
            data: data_left.to_vec(),
            capacity: capacity_left,
            storage_size: self.storage_size
        };
        let right = BitVector {
            data: data_right.to_vec(),
            capacity: capacity_right,
            storage_size: self.storage_size
        };
        (&left, &right)
    }

    pub fn split_at_mut(&mut self, index: usize) -> (&mut BitVector<S>, &mut BitVector<S>) {
        self.panic_index_not_on_storage_bound(index);
        let data_index = self.compute_data_index(index);
        let (capacity_left, capacity_right) = self.compute_capacities(index);
        let (data_left, data_right) = self.data.split_at_mut(data_index);

        let mut left = BitVector {
            data: data_left.to_vec(),
            capacity: capacity_left,
            storage_size: self.storage_size
        };
        let mut right = BitVector {
            data: data_right.to_vec(),
            capacity: capacity_right,
            storage_size: self.storage_size
        };
        (&mut left, &mut right)
    }

    #[inline]
    fn get_unchecked(&self, index: usize) -> bool {
        let (data_index, remainder) = self.compute_data_index_and_remainder(index);
        (self.data[data_index] & (S::one() << remainder)) != S::zero()
    }

    #[inline]
    fn compute_data_index_and_remainder(&self, index: usize) -> (usize, S) {
        let data_index = self.compute_data_index(index);
        let remainder = self.compute_data_remainder(index);
        (data_index, remainder)
    }

    #[inline]
    fn compute_data_index(&self, index: usize) -> usize {
        index / self.storage_size
    }

    #[inline]
    fn compute_data_remainder(&self, index: usize) -> S {
        let remainder = index % self.storage_size;
        // we know that remainder is always smaller or equal to the size that S can hold
        // for example if S = u8 then remainder <= 2^8 - 1
        let remainder: S = num::cast(remainder).unwrap();
        remainder
    }

    #[inline]
    fn compute_capacities(&self, index_to_split: usize) -> (usize, usize) {
        (index_to_split, self.capacity - index_to_split)
    }

    #[inline]
    fn index_in_bounds(&self, index: usize) -> bool {
        index < self.capacity
    }

    #[inline]
    fn panic_index_bounds(&self, index: usize) {
        if !self.index_in_bounds(index) {
            panic!("Index out of bounds. Length = {}, Index = {}", self.capacity, index);
        }
    }

    #[inline]
    fn panic_index_not_on_storage_bound(&self, index: usize) {
        if index % self.storage_size != 0 {
            panic!("Index not on storage bound. Storage size = {}, Index = {}", self.storage_size, index);
        }
    }
}

static TRUE: bool = true;
static FALSE: bool = false;

macro_rules! bool_ref {
    ($cond:expr) => (if $cond { &TRUE } else { &FALSE })
}

impl<S: BitStorage> Index<usize> for BitVector<S> {
    type Output = bool;

    fn index(&self, index: usize) -> &bool {
        self.panic_index_bounds(index);
        bool_ref!(self.get_unchecked(index))
    }
}

编译器错误发生在 split_atsplit_at_mut 方法:它们基本上告诉我 leftright在这两种情况下,寿命都不够长,无法作为参考返回.我理解这一点,因为它们是在堆栈上创建的,然后我想将它们作为引用返回.

The compiler errors occur at the split_at and split_at_mut methods: They basically tell me that left and right in both cases do not live long enough to be returned as a reference. I understand this, because they are created on the stack and then I want to return them as a reference.

然而,我的设计灵感来自 std::vec::Vec,你可以看到 在 SliceExt trait 中,它们的定义如下:

However with my design being inspired by std::vec::Vec you can see that in the SliceExt trait their definitions are as follows:

#[stable(feature = "core", since = "1.6.0")]
fn split_at(&self, mid: usize) -> (&[Self::Item], &[Self::Item]);

#[stable(feature = "core", since = "1.6.0")]
fn split_at_mut(&mut self, mid: usize) -> (&mut [Self::Item], &mut [Self::Item]);

我认为这样做是为了方便最终用户,因为他们更喜欢处理引用而不是框.

I suppose this is done for end user convenience as they rather deal with references than with boxes.

我想我可以通过将返回的位向量放入 Box<_> 来修复我的错误,但是有没有办法将创建的结构作为参考返回?

I think I could fix my errors by putting the returned bit vectors into a Box<_>, but is there a way to return the created structs as a reference?

作为一个额外的问题:如果我返回 (BitVector, BitVector),它确实有效,这样做有什么缺点?为什么 SliceExt 特性不这样做?

As a bonus question: It does work if I return (BitVector<S>, BitVector<S>), what are the downsides to doing this? Why does the SliceExt trait not do this?

推荐答案

如何返回一个新创建的结构体作为引用?

How to return a newly created struct as a reference?

你不能.没有办法解决这个问题;这根本不可能.正如您所说,如果它在堆栈上声明,那么该值将被删除并且任何引用都将无效.

You can't. No way around this; it's simply impossible. As you said, if it's declared on the stack then the value will be dropped and any references would be invalidated.

那么是什么让 Vec 与众不同?

So what makes Vec different?

A Vec 是切片 (&[T]) 的拥有副本.Vec 有指向数据开头的指针、计数和容量,而切片只有指针和计数.两者都保证所有数据是连续的.在伪 Rust 中,它们看起来像这样:

A Vec<T> is the owned counterpart of a slice (&[T]). While a Vec has a pointer to the beginning of data, a count, and a capacity, a slice only has the pointer and a count. Both guarantee that all the data is contiguous. In pseudo-Rust, they look like this:

struct Vec<T> {
    data: *mut T,
    size: usize,
    capacity: usize,
}

struct Slice<'a, T> {
    data: *mut T,
    size: usize,
}

Vec::split_at 可以返回切片,因为它本质上包含一个切片.它不是创建什么并返回对它的引用,它只是指针和计数的副本.

Vec::split_at can return slices because it essentially contains a slice. It's not creating something and returning a reference to it, it's just a copy of the pointer and the count.

如果你为你拥有的数据类型创建了一个借用的对应物,那么你可以返回它.类似的东西

If you create a borrowed counterpart to your owned datatype, then you could return that. Something like

struct BitVector {
    data: Vec<u8>,
    capacity: usize,
    storage_size: usize
}

struct BitSlice<'a> {
    data: &'a [u8],
    storage_size: usize,
}

impl BitVector {
    fn with_capacity(capacity: usize) -> BitVector {
        let storage_size = std::mem::size_of::<u8>() * 8;
        let len = (capacity / storage_size) + 1;
        BitVector { 
            data: vec![0; len],
            capacity: capacity,
            storage_size: storage_size
        }
    }

    fn split_at<'a>(&'a self) -> (BitSlice<'a>, BitSlice<'a>) {
        let (data_left, data_right) = self.data.split_at(0);
        let left = BitSlice {
            data: data_left,
            storage_size: self.storage_size
        };
        let right = BitSlice {
            data: data_right,
            storage_size: self.storage_size
        };
        (left, right)
    }
}

fn main() {}

为了遵循 Vec 的主题,您可能需要将 DerefDerefMut 转换为 BitSlice 和然后在 BitSlice 上实现所有非容量变化的方法.

To follow the theme of Vec, you would want to probably Deref and DerefMut to the BitSlice and then implement all the non-capacity-changing methods on the BitSlice.

我认为这样做是为了方便最终用户,因为他们更喜欢处理引用而不是框.

I suppose this is done for end user convenience as they rather deal with references than with boxes.

引用和框应该在使用站点上大部分是透明的.主要原因是性能.Box 是堆分配的.

References and boxes should be mostly transparent at the use-site. The main reason is performance. A Box is heap-allocated.

我想我可以通过将返回的位向量放入一个 Box 来修复我的错误<_>

I think I could fix my errors by putting the returned bit vectors into a Box<_>

这不是一个好主意.您已经通过 Vec 分配了一个堆,而对其进行装箱会引入另一个间接和额外的堆使用.

This would not be a good idea. You already have a heap allocation via the Vec, and boxing it would introduce another indirection and extra heap usage.

如果我返回 (BitVector, BitVector),它确实有效,这样做有什么缺点?为什么 SliceExt 特性不这样做?

It does work if I return (BitVector<S>, BitVector<S>), what are the downsides to doing this? Why does the SliceExt trait not do this?

是的,这里返回堆分配的结构.返回这些没有坏处,只有执行分配的坏处.这就是 SliceExt 不这样做的原因.

Yes, here you are returning the heap-allocated structures. There's no downside to returning these, there's just the downside of performing the allocations. That's why SliceExt doesn't do it.

这是否也直接转化为 split_at_mut 变体?

Does this also directly translate to the split_at_mut variant?

是的.

struct BitSliceMut<'a> {
    data: &'a mut [u8],
    storage_size: usize,
}

fn split_at_mut<'a>(&'a mut self) -> (BitSliceMut<'a>, BitSliceMut<'a>) {
    let (data_left, data_right) = self.data.split_at_mut (0);
    let left = BitSliceMut {
        data: data_left,
        storage_size: self.storage_size
    };
    let right = BitSliceMut {
        data: data_right,
        storage_size: self.storage_size
    };
    (left, right)
}

这有助于指出 &T&mut T不同的类型并且行为方式不同.

This helps point out that &T and &mut T are different types and behave in different ways.

不允许将 (mut BitSlice<'a>, mut BitSlice<'a> 作为返回类型.

it's not allowed to give (mut BitSlice<'a>, mut BitSlice<'a> as return type.

返回一个 mut T 没有意义:之前的 `mut` 有什么区别一个变量名,在`:`? 之后.使用 BitSliceMut,可变性是包含类型 (&mut [u8]) 的一个方面.

It doesn't make sense to return a mut T: What's the difference in `mut` before a variable name and after the `:`?. With a BitSliceMut, the mutability is an aspect of the contained type (&mut [u8]).

这篇关于如何返回一个新创建的结构体作为参考?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆