我试图为T
类型的对象编写容器,该容器提供对存储对象的引用&T
的访问(我想避免复制)。由于容器在其生命周期内只会增长,因此返回的引用&T
的生命周期应与容器的生命周期相同。
我到目前为止最接近的是在容器内部使用Box<T>
个对象,并使用Box<T>.as_ref()
返回对这些对象的引用。然而,然而,我遇到了与这个最小例子中相同的问题:
fn main() {
let mut v = vec![Box::new(1)]; // line 1
let x = v[0].as_ref(); // line 2: immutable borrow occurs here
println!("x = {:?}", x); // line 3
v.push(Box::new(2)); // line 4: mutable borrow occurs here -> error
println!("x = {:?}", x); // line 5
}
我理解,如果在可变借位期间从x
删除v
,则在第5行使用x
将是不合理的。但情况并非如此,而且它永远不会出现在我的容器中。如果在Rust中没有安全的方式来表达这一点,我怎么能修复&#34;示例(不复制@model List<KeyValuePair<string, object>>
<div>
@foreach (KeyValuePair<string, object> item in Model)
{
Type type = item.Value.GetType();
switch (Type.GetTypeCode(type))
{
case TypeCode.Boolean:
{
<input type="checkbox"/>
break;
}
case TypeCode.String:
{
<input type="text"/>
break;
}
default:
{
if (type == typeof (MyCustomType))
{
...
}
else if (type == typeof (MyOtherType))
{
...
}
}
}
}
</div>
)?
答案 0 :(得分:5)
正如@ ker的回答所说,仅仅因为你只是一个容器,并不意味着引用保持有效,因为内存可以重新分配。
如果你只是在增长容器,那么另一种解决方案就是只存储索引而不是引用:
log(n)^2
答案 1 :(得分:2)
问题是您的Vec
可能内存不足。如果发生这种情况,它必须分配一个新的(更大的)内存块,复制所有旧数据,并删除旧的内存分配。此时,对旧容器的所有引用都将变为无效。
您可以设计一个rope(Vec
Vec
},其中,一旦内部vec已满,您只需创建一个新的,因此永远不会通过推送使指针无效
这需要一些不安全的代码,并且必须非常仔细地分析你必须跟上的借用规则。
答案 2 :(得分:1)
不幸的是,没有什么“可以随时使用”用于您的用例,但您可以编写一个包含Vec
的包装器来执行不安全的操作,以便为您提供所需的功能,同时保证生锈需要
struct BoxVec<T>(Vec<Box<T>>);
impl<T> BoxVec<T> {
pub fn new() -> Self { BoxVec(Vec::new()) }
pub fn push<'a>(&'a mut self, t: T) -> &'a mut T {
let mut b = Box::new(t);
let t: &'a mut T = unsafe { std::mem::transmute::<&mut T, &'a mut T>(&mut b) };
self.0.push(b);
t
}
pub fn pusher<'a>(&'a mut self) -> BoxVecPusher<'a, T> {
BoxVecPusher(self)
}
}
struct BoxVecPusher<'a, T: 'a>(&'a mut BoxVec<T>);
impl<'a, T> BoxVecPusher<'a, T> {
fn push<'b>(&'b mut self, t: T) -> &'a mut T {
let mut b = Box::new(t);
let t: &'a mut T = unsafe { std::mem::transmute::<&mut T, &'a mut T>(&mut b) };
(self.0).0.push(b);
t
}
}
我选择的保证是你不能索引到Pusher
,但是你得到了一个对新推送对象的可变引用。一旦释放推动器,您可以返回索引。
示例用法是
let mut v = BoxVec::new();
v.push(1);
let x = v[0];
let mut p = v.pusher();
let i = p.push(2);
p.push(3); // works now
let y = v[0]; // denied by borrow checker