- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我有一组需要相互了解才能合作的对象。这些对象存储在容器中。我试图对如何在 Rust 中构建我的代码有一个非常简单的想法。
让我们打个比方。 Computer
包含:
Mmu
Ram
Processor
在 Rust 中:
struct Computer {
mmu: Mmu,
ram: Ram,
cpu: Cpu,
}
对于任何工作,Cpu
需要了解 Mmu
它链接到 Mmu
需要知道 Ram
它链接到。
我不想要Cpu
按值聚合 Mmu
.他们的生命周期不同:Mmu
可以自己过自己的生活。碰巧我可以将它插入 Cpu
.但是,创建 Cpu
是没有意义的。没有 Mmu
依附于它,因为它无法完成它的工作。 Mmu
之间存在相同的关系和 Ram
.
因此:
Ram
可以自己生活。Mmu
需要 Ram
.Cpu
需要一个 Mmu
.我如何在 Rust 中为这种设计建模,一个具有其字段相互了解的结构。
在 C++ 中,它应该是这样的:
>
struct Ram
{
};
struct Mmu
{
Ram& ram;
Mmu(Ram& r) : ram(r) {}
};
struct Cpu
{
Mmu& mmu;
Cpu(Mmu& m) : mmu(m) {}
};
struct Computer
{
Ram ram;
Mmu mmu;
Cpu cpu;
Computer() : ram(), mmu(ram), cpu(mmu) {}
};
这是我开始用 Rust 翻译它的方式:
struct Ram;
struct Mmu<'a> {
ram: &'a Ram,
}
struct Cpu<'a> {
mmu: &'a Mmu<'a>,
}
impl Ram {
fn new() -> Ram {
Ram
}
}
impl<'a> Mmu<'a> {
fn new(ram: &'a Ram) -> Mmu<'a> {
Mmu {
ram: ram
}
}
}
impl<'a> Cpu<'a> {
fn new(mmu: &'a Mmu) -> Cpu<'a> {
Cpu {
mmu: mmu,
}
}
}
fn main() {
let ram = Ram::new();
let mmu = Mmu::new(&ram);
let cpu = Cpu::new(&mmu);
}
很好,但现在我找不到创建 Computer
的方法结构。
我开始于:
struct Computer<'a> {
ram: Ram,
mmu: Mmu<'a>,
cpu: Cpu<'a>,
}
impl<'a> Computer<'a> {
fn new() -> Computer<'a> {
// Cannot do that, since struct fields are not accessible from the initializer
Computer {
ram: Ram::new(),
mmu: Mmu::new(&ram),
cpu: Cpu::new(&mmu),
}
// Of course cannot do that, since local variables won't live long enough
let ram = Ram::new();
let mmu = Mmu::new(&ram);
let cpu = Cpu::new(&mmu);
Computer {
ram: ram,
mmu: mmu,
cpu: cpu,
}
}
}
好吧,无论如何,我将无法找到一种方法来引用它们之间的结构字段。我想我可以通过创建 Ram
来想出一些办法。 , Mmu
和 Cpu
在堆上;并将其放入结构中:
struct Computer<'a> {
ram: Box<Ram>,
mmu: Box<Mmu<'a>>,
cpu: Box<Cpu<'a>>,
}
impl<'a> Computer<'a> {
fn new() -> Computer<'a> {
let ram = Box::new(Ram::new());
// V-- ERROR: reference must be valid for the lifetime 'a
let mmu = Box::new(Mmu::new(&*ram));
let cpu = Box::new(Cpu::new(&*mmu));
Computer {
ram: ram,
mmu: mmu,
cpu: cpu,
}
}
}
是的,没错,此时 Rust 无法知道我将转移 let ram = Box::new(Ram::new())
的所有权到 Computer
, 所以它的生命周期为 'a
.
我一直在尝试各种或多或少有点老套的方法来解决这个问题,但我就是想不出一个干净的解决方案。我最接近的是删除引用并使用 Option
,但是我所有的方法都必须检查 Option
是否是Some
或 None
,这是相当丑陋的。
我想我只是走错了路,试图在 Rust 中映射我在 C++ 中所做的事情,但这行不通。这就是为什么我需要帮助找出创建此架构的惯用 Rust 方法。
最佳答案
在这个答案中,我将讨论解决此问题的两种方法,一种是安全的 Rust,具有零动态分配和非常低的运行时成本,但可能会受到限制,另一种是使用不安全不变量的动态分配。
Cell<Option<&'a T>
)use std::cell::Cell;
#[derive(Debug)]
struct Computer<'a> {
ram: Ram,
mmu: Mmu<'a>,
cpu: Cpu<'a>,
}
#[derive(Debug)]
struct Ram;
#[derive(Debug)]
struct Cpu<'a> {
mmu: Cell<Option<&'a Mmu<'a>>>,
}
#[derive(Debug)]
struct Mmu<'a> {
ram: Cell<Option<&'a Ram>>,
}
impl<'a> Computer<'a> {
fn new() -> Computer<'a> {
Computer {
ram: Ram,
cpu: Cpu {
mmu: Cell::new(None),
},
mmu: Mmu {
ram: Cell::new(None),
},
}
}
fn freeze(&'a self) {
self.mmu.ram.set(Some(&self.ram));
self.cpu.mmu.set(Some(&self.mmu));
}
}
fn main() {
let computer = Computer::new();
computer.freeze();
println!("{:?}, {:?}, {:?}", computer.ram, computer.mmu, computer.cpu);
}
与流行的看法相反,自引用在安全的 Rust 中实际上是可能的,而且更好的是,当您使用它们时,Rust 将继续为您强制执行内存安全。
使用 &'a T
获取自引用、递归引用或循环引用所需的主要“技巧”是使用 Cell<Option<&'a T>
包含引用。如果没有 Cell<Option<T>>
,您将无法执行此操作包装器。
此解决方案的巧妙之处在于将结构的初始创建与正确的初始化分开。这有一个不幸的缺点,即在调用 freeze
之前初始化它并使用它可能会错误地使用这个结构。 , 但如果不进一步使用 unsafe
就不会导致内存不安全.
结构的初始创建只是为我们后来的 hackery 设置了舞台 - 它创建了 Ram
,它没有依赖关系,并设置 Cpu
和 Mmu
到他们无法使用的状态,包含Cell::new(None)
而不是他们需要的引用资料。
然后,我们调用 freeze
方法,有意地借用自己的一生'a
,或结构的整个生命周期。调用此方法后,编译器将阻止我们获取对 Computer
的可变引用。 或移动Computer
,因为任何一个都可能使我们持有的引用无效。 freeze
然后方法设置 Cpu
和 Mmu
通过设置 Cell
来适本地包含 Some(&self.cpu)
或 Some(&self.ram)
分别。
在 freeze
之后被称为我们的结构已准备好使用,但只是不可变的。
Box<T>
永远不会移动T
)#![allow(dead_code)]
use std::mem;
// CRUCIAL INFO:
//
// In order for this scheme to be safe, Computer *must not*
// expose any functionality that allows setting the ram or
// mmu to a different Box with a different memory location.
//
// Care must also be taken to prevent aliasing of &mut references
// to mmu and ram. This is not a completely safe interface,
// and its use must be restricted.
struct Computer {
ram: Box<Ram>,
cpu: Cpu,
mmu: Box<Mmu>,
}
struct Ram;
// Cpu and Mmu are unsafe to use directly, and *must only*
// be exposed when properly set up inside a Computer
struct Cpu {
mmu: *mut Mmu,
}
struct Mmu {
ram: *mut Ram,
}
impl Cpu {
// Safe if we uphold the invariant that Cpu must be
// constructed in a Computer.
fn mmu(&self) -> &Mmu {
unsafe { mem::transmute(self.mmu) }
}
}
impl Mmu {
// Safe if we uphold the invariant that Mmu must be
// constructed in a Computer.
fn ram(&self) -> &Ram {
unsafe { mem::transmute(self.ram) }
}
}
impl Computer {
fn new() -> Computer {
let ram = Box::new(Ram);
let mmu = Box::new(Mmu {
ram: unsafe { mem::transmute(&*ram) },
});
let cpu = Cpu {
mmu: unsafe { mem::transmute(&*mmu) },
};
// Safe to move the components in here because all the
// references are references to data behind a Box, so the
// data will not move.
Computer {
ram: ram,
mmu: mmu,
cpu: cpu,
}
}
}
fn main() {}
注意:考虑到 Computer
的不受限制接口(interface),此解决方案并不完全安全- 必须注意不要使用别名或删除 Mmu
或 Ram
在计算机的公共(public)界面中。
此解决方案改为使用不变量,即数据存储在 Box
中永远不会移动 - 它的地址永远不会改变 - 只要 Box
还活着。 Rust 不允许您在安全代码中依赖于此,因为移动了 Box
可能导致它后面的内存被释放,从而留下悬空指针,但我们可以在不安全的代码中依赖它。
这个解决方案的主要技巧是使用原始指针进入 Box<Mmu>
的内容。和 Box<Ram>
在 Cpu
中存储对它们的引用和 Mmu
分别。这为您提供了一个基本安全的界面,并且不会阻止您移动 Computer
在受限情况下围绕甚至改变它。
综上所述,我认为这些中的任何一个都不应该真正成为您解决此问题的方式。所有权是 Rust 的核心概念,它渗透到几乎所有代码的设计选择中。如果Mmu
拥有 Ram
和 Cpu
拥有 Mmu
,这就是您的代码中应该具有的关系。如果你使用 Rc
,您甚至可以保持共享底层部分的能力,尽管是不可变的。
关于rust - 包含相互了解的字段的结构,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28113504/
在有些场景下,我们需要对我们的varchar类型的字段做修改,而修改的结果为两个字段的拼接或者一个字段+字符串的拼接。 如下所示,我们希望将xx_role表中的name修改为name+id。
SELECT incMonth as Month, SUM( IF(item_type IN('typ1', 'typ2') AND incMonth = Month, 1, 0 ) )AS
我最近读到 volatile 字段是线程安全的,因为 When we use volatile keyword with a variable, all the threads read its va
我在一些模型中添加了一个 UUID 字段,然后使用 South 进行了迁移。我创建的任何新对象都正确填充了 UUID 字段。但是,我所有旧数据的 UUID 字段为空。 有没有办法为现有数据填充 UUI
刚刚将我的网站从 mysql_ 更新为 mysqli,并破坏了之前正常运行的查询。 我试图从旋转中提取 id,因为它每次都会增加 1,但我不断获取玩家 id,有人可以告诉我我做错了什么吗?我尝试了将
我在 Mac OS X 上使用带有 Sequel Pro 的 MySQL。我想将一个表中的一个字段(即名为“GAME_DY”的列)复制到另一个名为“DAY_ID”的表的空字段中。两个表都是同一数据库的
问题: 是否有可能有一个字段被 JPA 保留但被序列化跳过? 可以实现相反的效果(JPA 跳过字段而序列化则不会),如果使用此功能,那么相反的操作肯定会很有用。 类似这样的事情: @Entity cl
假设我有一个名为“dp”的表 Year | Month | Payment| Payer_ID | Payment_Recipient | 2008/2009 | July
我将尝试通过我的 Raspberry Pi 接入点保证一些 QoS。 开始之前,我先动手:我阅读了有关 tcp、udp 和 ip header 的内容。在IP header description我看
如果你能弄清楚如何重命名这个问题,我愿意接受建议。 在 Dart 语言中,可以编写一个带有 final 字段的类。这些是只能设置的字段构造函数前 body 跑。这可以在声明中(通常用于类中的静态常量)
你怎么样? 我有两个带有两个字段的日期选择器 我希望当用户选择 (From) 时,第二个字段 (TO) 将是 next day 。比如 booking.com 例如:当用户选择From 01-01-2
我想我已经看到了这个问题的一些答案,这些答案可能与我需要的相差不远,但我对 mysql 的了解还不够确定,所以我会根据我的具体情况提出问题。 我有一个包含多个表的数据库,为此,如果“image”表上的
我在 mySQL 数据库中有 2 个表: customers ============ customer_id (1, 2 ) customer_name (john, mark) orders ==
我正在开发一个员工目标 Web 应用程序。 领导/经理在与团队成员讨论后为他们设定目标。这是一年/半年/季度,具体取决于组织遵循的评估周期。 现在的问题是添加基于时间段的字段或存档上一季度/年度数据的
我正在寻找允许内容编辑器从媒体库中选择多个文件的东西,这些文件将在渲染中列出。他们还需要能够上传文件和搜索。它必须在页面编辑器(版本 8 中称为体验编辑器)中工作。 到目前为止我所考虑的: 一堆文件字
现在,我有以下由 original.df %.% group_by(Category) %.% tally() %.% arrange(desc(n)) 创建的 data.frame。 DF 5),
我想知道是否有一些步骤/解决方案可以处理错误消息并将它们放入 Pentaho 工具中的某个字符串或字段中?例如,如果连接到数据库时发生某些错误,则将该消息从登录到字符串/字段。 最佳答案 我们在作业的
如何制作像短信应用程序一样的“收件人”字段?例如,右侧有一个“+”按钮,当添加某人时,名称将突出显示并可单击,如圆角矩形等。有没有内置的框架? 最佳答案 不,但请参阅 Three20 的 TTMess
是否可以获取记录的元素或字段的列表 通过类型信息类似于类的已发布属性的列表吗? 谢谢 ! 最佳答案 取决于您的delphi版本,如果您使用的是delphi 2010或更高版本,则可以使用“新rtti”
我正在构建一个 SQLite 数据库来保存我的房地产经纪人的列表。我已经能够使用外键来识别每个代理的列表,但我想在每个代理的记录中创建一个列表;从代理商和列表之间的一对一关系转变为一对多关系。 看这里
我是一名优秀的程序员,十分优秀!