考虑一下,为了简单起见,我想实现一个可转位向量v,它有n个连续的元素0,1,。。。,n-1,即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
}
然而,上面的代码只是我试图实现的实际结构的模型。除此之外,(1)我希望能够使用索引运算符,(2)尽管向量实际上可能在访问位置时被修改,但我希望这对用户是透明的,也就是说,即使我对v有不可变的引用,我也希望能够索引任何位置。为了防止其他不必要的修改,首选不可变引用。
需求(1)可以通过实现Index trait来实现,就像这样
impl std::ops::Index<usize> for LazyVector {
type Output = usize;
fn index(&self, i: usize) -> &Self::Output {
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
}
}
但是,这不起作用,因为它试图返回一个值,该值引用了在LazyVector::get结尾处超出范围的借()返回的Ref struct。最后,为了避免这种情况,我做了如下的事情
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
}
现在它按要求工作,但是,作为一个新手,我不太确定不安全的块!我认为我有效地用一个安全的界面包装了它,但我不确定。所以我的问题是这是否可以,或者是否有更好的、完全安全的方法来实现这一点。
谢谢你的帮助。
编辑由于您提供了有关您的目标的更多信息(懒惰访问磁盘上的大文件块),我更新了我的答案。
你可以使用细胞。我引用医生的话:
由于细胞类型能够在不允许的情况下实现突变,因此有时内部突变可能是合适的,甚至必须使用,例如[…]逻辑不可变方法的实现细节。[...]
这是一段完成这项工作的代码(请注意,这与您编写的非常接近):
use std::cell::RefCell;
use std::ops::Index;
// This is your file
const DATA: &str = "Rust. A language empowering everyone to build reliable and efficient software.";
#[derive(Debug)]
struct LazyVector<'a, 'b> {
ref_v: RefCell<&'a mut Vec<&'b str>>
}
impl<'a, 'b> LazyVector<'a, 'b> {
fn new(v: &'a mut Vec<&'b str>) -> LazyVector<'a, 'b> {
LazyVector {
ref_v: RefCell::new(v)
}
}
/// get or load a chunk of two letters
fn get_or_load(&self, i: usize) -> &'b str {
let mut v = self.ref_v.borrow_mut();
for k in v.len()..=i {
v.push(&DATA[k * 2..k * 2 + 2]);
}
v[i]
}
}
impl<'a, 'b> Index<usize> for LazyVector<'a, 'b> {
type Output = str;
fn index(&self, i: usize) -> &Self::Output {
self.get_or_load(i)
}
}
pub fn main() {
let mut v = vec![];
let lv = LazyVector::new(&mut v);
println!("v[5]={}", &lv[5]); // v[5]=ng
println!("{:?}", lv); // LazyVector { ref_v: RefCell { value: ["Ru", "st", ". ", "A ", "la", "ng"] } }
println!("v[10]={}", &lv[10]); // v[10]=ow
println!("{:?}", lv); // LazyVector { ref_v: RefCell { value: ["Ru", "st", ". ", "A ", "la", "ng", "ua", "ge", " e", "mp", "ow"] } }
}
与您尝试的主要区别在于底层Vec
是一个外部可变向量,而LazyVector
在此向量上只获取一个(可变)ref。RwLock应该是处理并发访问的方法。
但是,我不推荐这种解决方案:
首先,您的底层Vec
将迅速增长并变得与磁盘上的文件一样大。因此,您需要一个映射而不是向量,并将该映射中的块数保持在给定边界下。如果您要求一个不在内存中的块,您将不得不选择一个要删除的块。这只是分页,操作系统通常比您更擅长这个游戏(参见页面替换算法)。正如我在评论中所写,内存映射文件(以及在“繁重”进程的情况下可能共享内存)会更有效:操作系统处理文件的延迟加载和只读数据的共享。R.Sedgewick在C中的算法,第一版,第13章,“一种更简单的方法”一节中的评论解释了为什么对一个巨大的文件(比内存大)进行排序可能比一个想法更容易:
在一个好的虚拟内存系统中,程序员可以处理大量的数据,让系统负责确保所处理的数据在需要时从外部存储转移到内部存储。
第二,请看我之前的回答。
先前的答案
我曾经对这种向量进行过编码。。。在爪哇。用例是表示一个非常稀疏的网格(许多行只有几个单元格宽,但网格的宽度应该是1024)。为了避免在需要时手动添加单元格,我创建了一个“列表”,大致实现了您试图实现的目标(但只有一个默认值)。
起初,我让我的列表实现了list
接口,但我很快意识到,为了不打破Liskov替换原则,我必须编写大量无用(且速度较慢)的代码。更糟糕的是,一些方法的行为对常用列表有误导性(ArrayList
,LinkedList
,…)。
你似乎也处于同样的情况:你希望你的LazyVector
看起来像一个普通的Vec
,这就是为什么你想要实现Index
和IndexMut
特征。但您正在寻找解决方法来实现这一点(例如,safe
code以匹配特征方法签名)。
我的建议是:不要试图让LazyVector
看起来像一个普通的向量,而是要明确LazyVector
不是一个普通的向量。这是最少惊讶的原则。例如,将get
(预计只能由用户善意地读取数据)替换为get_or_extend
,这表明要么你得到一些东西,要么你创建它。如果你添加一个get_or_extend_mut
函数,你就有了一些不是很有吸引力但高效和可预测的东西:
impl LazyVector {
fn new() -> LazyVector { ... }
fn get_or_extend(&mut self, i: usize) -> &usize { ... }
fn get_or_extend_mut(&mut self, i: usize) -> &mut usize { ... }
}
变量的内部实现 变量是一个语言实现的基础,变量有两个组成部分:变量名、变量值,PHP中可以将其对应为:zval、zend_value,这两个概念一定要区分开,PHP中变量的内存是通过引用计数进行管理的,而且PHP7中引用计数是在zend_value而不是zval上,变量之间的传递、赋值通常也是针对zend_value。 PHP中可以通过$关键词定义一个变量:$a;,在定义的同时可以进行初始化:$a
San CLI 是一个命令行工具,其次它是一个内置 Webpack 的前端工程化构建工具。San CLI 在架构设计上采取了微核心和插件化的设计思想,我们可以通过插件机制添加命令行命令,还可以通过插件机制定制 Webpack 构建工具,从而满足不同 San 环境的前端工程化需求。 San CLI 在兼顾 San 生态的同时,尽量做到通用化配置,在设计之初,我们希望不局限于 San 的应用范畴,做可
突变性可以是性状中的一个通用参数吗?我想为一个类型的可变和不可变变体实现一个特性,而不必复制 一厢情愿的伪代码:
本文向大家介绍Linux利用keepalived实现lvs的高可用性的操作方法,包括了Linux利用keepalived实现lvs的高可用性的操作方法的使用技巧和注意事项,需要的朋友参考一下 lvs+keepalived是什么? keepalived工作原理 keepalived是集群管理中保证集群高可用的一个服务软件,其功能类似于heartbeat,用来防止单点故障。 keepalived是以V
为什么<code>test
本文向大家介绍使用Lucene.NET实现站内搜索,包括了使用Lucene.NET实现站内搜索的使用技巧和注意事项,需要的朋友参考一下 导入Lucene.NET 开发包 Lucene 是apache软件基金会一个开放源代码的全文检索引擎工具包,是一个全文检索引擎的架构,提供了完整的查询引擎和索引引擎,部分文本分析引擎。Lucene的目的是为软件开发人员提供一个简单易用的工具包,以方便的在目标系统中