Rust 利用内部可变性实现索引

Rust 利用内部可变性实现索引,rust,Rust,考虑到,为了简单起见,我想要实现一个具有n个连续元素0,1,…,n-1的可转位向量v,即v[I]=I。该向量应按需填充,即,如果使用v[i],且当前该向量包含nLazyVector{ 懒虫{ 数据:vec![] } } fn获取(&mut self,i:usize)->&usize{ 对于self.data.len()中的x=i{ 自我数据推送(i); } &自我数据[i] } } pub fn main(){ 设mut v=LazyVector::new(); println!([v[5]={

考虑到,为了简单起见,我想要实现一个具有n个连续元素0,1,…,n-1的可转位向量v,即v[I]=I。该向量应按需填充,即,如果使用v[i],且当前该向量包含n 下面的代码可以正常工作

struct LazyVector {
    data: Vec<usize>
}

impl LazyVector {
    fn new() -> LazyVector {
        LazyVector{
            data: vec![] 
        }
    }
    fn get(&mut self, i:usize) -> &usize {
        for x in self.data.len()..=i {
            self.data.push(i);
        }
        &self.data[i]
    }
}


pub fn main() {
    let mut v = LazyVector::new();
    println!("v[5]={}",v.get(5)); // prints v[5]=5
}
struct LazyVector{
数据:Vec
}
简单懒散向量{
fn new()->LazyVector{
懒虫{
数据:vec![]
}
}
fn获取(&mut self,i:usize)->&usize{
对于self.data.len()中的x=i{
自我数据推送(i);
}
&自我数据[i]
}
}
pub fn main(){
设mut v=LazyVector::new();
println!([v[5]={},v.get(5));//打印v[5]=5
}
然而,上面的代码只是我试图实现的实际结构的一个模型。除此之外,(1)我希望能够使用索引操作符,(2)尽管向量在访问位置时可能会被修改,但我希望这对用户是透明的,也就是说,我希望能够索引任何位置,即使我对v有一个不可变的引用。首选不可变引用,以防止其他不必要的修改

需求(1)可以通过实现指标特性来实现,如下所示

impl std::ops::Index<usize> for LazyVector {
    type Output = usize;
    fn index(&self, i: usize) -> &Self::Output {
        self.get(i)
    }
}
LazyVector的impl std::ops::Index{ 类型输出=usize; fn索引(&self,i:usize)->&self::输出{ self.get(i) } } 然而,这并没有编译,因为我们需要一个可变引用才能调用LazyVector::get。由于需求(2),我们不想让这个引用可变,即使我们这样做了,我们也不能这样做,因为它会违反索引特性的接口。我认为这将通过RefCell智能指针(如铁锈书第15章)为内部可变性模式提供支持。所以我想出了类似的办法

struct LazyVector {
    data: std::cell::RefCell<Vec<usize>>
}

impl LazyVector {
    fn new() -> LazyVector {
        LazyVector{
            data: std::cell::RefCell::new(vec![]) 
        }
    }

    fn get(&self, i:usize) -> &usize {
        let mut mutref = self.data.borrow_mut();
        for x in mutref.len()..=i {
            mutref.push(x)
        }
        &self.data.borrow()[i] // error: cannot return value referencing a temporary value
    }
}
struct LazyVector {
    data: std::cell::RefCell<Vec<usize>>
}


impl LazyVector {
    fn new() -> LazyVector {
        LazyVector{
            data: std::cell::RefCell::new(vec![]) 
        }
    }

    fn get(&self, i:usize) -> &usize {
        let mut mutref = self.data.borrow_mut();
        for x in mutref.len()..=i {
            mutref.push(x)
        }
        unsafe { // Argh!
            let ptr = self.data.as_ptr();
            &std::ops::Deref::deref(&*ptr)[i]
        }
    }
}


impl std::ops::Index<usize> for LazyVector {
    type Output = usize;
    fn index(&self, i: usize) -> &Self::Output {
        self.get(i)
    }
}

pub fn main() {
    let v = LazyVector::new();    // Unmutable!
    println!("v[5]={}",v.get(5)); // prints v[5]=5
}
struct LazyVector{
数据:std::cell::RefCell
}
简单懒散向量{
fn new()->LazyVector{
懒虫{
数据:std::cell::RefCell::new(vec![])
}
}
fn get(&self,i:usize)->&usize{
让mut mutref=self.data.borrow_mut();
对于mutref.len()中的x=i{
多参考推力(x)
}
&self.data.borrow()[i]//错误:无法返回引用临时值的值
}
}
但是,这不起作用,因为它试图返回一个值,该值引用了在LazyVector::get末尾超出范围的借()返回的Ref结构。最后,为了避免这种情况,我做了如下的事情

struct LazyVector {
    data: std::cell::RefCell<Vec<usize>>
}

impl LazyVector {
    fn new() -> LazyVector {
        LazyVector{
            data: std::cell::RefCell::new(vec![]) 
        }
    }

    fn get(&self, i:usize) -> &usize {
        let mut mutref = self.data.borrow_mut();
        for x in mutref.len()..=i {
            mutref.push(x)
        }
        &self.data.borrow()[i] // error: cannot return value referencing a temporary value
    }
}
struct LazyVector {
    data: std::cell::RefCell<Vec<usize>>
}


impl LazyVector {
    fn new() -> LazyVector {
        LazyVector{
            data: std::cell::RefCell::new(vec![]) 
        }
    }

    fn get(&self, i:usize) -> &usize {
        let mut mutref = self.data.borrow_mut();
        for x in mutref.len()..=i {
            mutref.push(x)
        }
        unsafe { // Argh!
            let ptr = self.data.as_ptr();
            &std::ops::Deref::deref(&*ptr)[i]
        }
    }
}


impl std::ops::Index<usize> for LazyVector {
    type Output = usize;
    fn index(&self, i: usize) -> &Self::Output {
        self.get(i)
    }
}

pub fn main() {
    let v = LazyVector::new();    // Unmutable!
    println!("v[5]={}",v.get(5)); // prints v[5]=5
}
struct LazyVector{
数据:std::cell::RefCell
}
简单懒散向量{
fn new()->LazyVector{
懒虫{
数据:std::cell::RefCell::new(vec![])
}
}
fn get(&self,i:usize)->&usize{
让mut mutref=self.data.borrow_mut();
对于mutref.len()中的x=i{
多参考推力(x)
}
不安全{//Argh!
设ptr=self.data.as_ptr();
&std::ops::Deref::Deref(&*ptr)[i]
}
}
}
LazyVector的impl std::ops::索引{
类型输出=usize;
fn索引(&self,i:usize)->&self::输出{
self.get(i)
}
}
pub fn main(){
设v=LazyVector::new();//不可变!
println!([v[5]={},v.get(5));//打印v[5]=5
}
现在它可以按要求工作了,但作为一个新手,我对不安全的块不是很确定!我认为我正在用一个安全的界面有效地包装它,但我不确定。所以我的问题是,这是否可以,或者是否有更好的、完全安全的方法来实现这一点


谢谢你的帮助。

编辑因为你提供了更多关于你目标的信息(懒散地访问磁盘上的大文件块),我更新了我的答案

您可以使用(正如您所尝试的)单元格。我引述:

由于细胞类型能够在不允许的情况下实现突变,因此有时可能需要使用内部可变性,甚至必须使用内部可变性,例如[…]逻辑不可变方法的实现细节。[……]

下面是一段执行此任务的代码(请注意,这与您编写的代码非常接近):


由于您正在返回对
usize
的引用,如果您的代码按原样工作,则当对
usize
的引用存在时,它将扩展向量并重新分配向量中的内存,这将导致无效的内存访问。如果要执行此操作,则需要返回
usize
而不是引用,这意味着您不能使用
索引
特性。不安全块不可靠。添加到向量可能会导致它重新分配,因此引用可能最终成为一个悬空指针。当变异的方法采用
&mut self
时,这是生锈保护您的一个因素。无论您在这里做什么,它都会变得非常复杂。这应该是一个暗示,你正在尝试一些奇怪的东西,你应该重新思考为什么你甚至需要这个。哦,老兄!哼!很明显,你已经指出了。我太专注于在真实场景中应该使用的方式,以至于忽略了这个明显的问题。(见下一个答案的评论)谢谢大家的评论。非常明智的建议。然而,这实际上是我试图用更简单的术语解释这个问题。实际上,这应该是位于磁盘上的大型文本数据的代理,不需要立即加载到内存中。在任何给定的时间,我只需要对其中的一部分进行只读访问,我还没有考虑并发访问。与我的简化相反,一般来说,我希望引用文本的各个部分,而不是单个字母,这就是为什么我不想返回第一条评论中(正确地)建议的元素的自有副本。非常重要的是,我努力的全部目的是能够使用索引特征和语法