Reference Rust中计算图节点的重载添加
我正在创建一个计算图,一个将方程建模为图的程序,这样就可以区分方程。我重载了操作符,因此添加图节点会创建新的图节点。然而,我发现自己在和借书人打交道。代码如下:Reference Rust中计算图节点的重载添加,reference,rust,operator-overloading,Reference,Rust,Operator Overloading,我正在创建一个计算图,一个将方程建模为图的程序,这样就可以区分方程。我重载了操作符,因此添加图节点会创建新的图节点。然而,我发现自己在和借书人打交道。代码如下: use std::ops::Add; #[derive(Debug)] pub enum Expr<'a> { Constant(f32), // ^^^--this is a simplified version. In the real version, //
use std::ops::Add;
#[derive(Debug)]
pub enum Expr<'a> {
Constant(f32),
// ^^^--this is a simplified version. In the real version,
// Constant holds large matrices and I would like to
// avoid copying it.
Add(&'a Node<'a>, &'a Node<'a>),
}
#[derive(Debug)]
pub struct Node<'a> {
pub body: Expr<'a>
}
impl<'a> Add for &'a Node<'a> {
type Output = Node<'a>;
fn add(self, other: &'a Node<'a>) -> Node<'a> {
Node { body: Expr::Add(self, other) }
}
}
fn main() {
let a = Node { body: Expr::Constant(1.) };
let f: Node = &(&a + &a) + &a;
println!("f: {:#?}", f);
}
我使用引用是因为我想避免复制节点
结构。这里的代码稍微简化了一点;在我使用的版本中,这些可以保存大量数据
我尝试过静态引用和Rc
指针,但没有成功。当我尝试使用Rc
指针时,编译器抱怨重载Add
:
fn add(self, other: &'a Expr<'a>) -> Expr<'a> {
Expr::Constant(42.0)
}
error[E0117]:对于任意类型,只能实现当前板条箱中定义的特征
-->功能rs:86:1
|
86 |针对Rc的简单添加{
|^impl不在板条箱内使用类型
|
=注:IML未引用此板条箱中定义的任何类型
目前,我只是为每一个非常混乱的操作定义新变量:
let f1 = &a + &a;
...
let f = &a + f1
理想的情况是,如果我可以完全避免使用引用(这将使代码更干净)。至少,我需要某种方法从添加现有节点生成新节点。让我们首先认识到
节点和Expr
是冗余的:
use std::ops::Add;
#[derive(Debug)]
pub enum Expr<'a> {
Constant(f32),
Add(&'a Expr<'a>, &'a Expr<'a>),
}
impl<'a> Add for &'a Expr<'a> {
type Output = Expr<'a>;
fn add(self, other: &'a Expr<'a>) -> Expr<'a> {
Expr::Add(self, other)
}
}
fn main() {
let a = Expr::Constant(1.);
let f = &(&a + &a) + &a;
println!("f: {:#?}", f);
}
然后我们调用它并将其作为表达式的前半部分返回:
let f = &(&a + &a) + &a;
// ^-------- here
常量
存储在哪里?只有一个没有所有者的临时值。当你进行第二次加法时,你试图引用一个必须在f
有效的整个时间内都存在的值。除了该表达式一结束,该值就会被删除,因为没有任何东西可以拥有它。因此你不能做这种事
此外,&'a Expr
,我认为有两件事:
这应该是Foo
——我们对某个有引用的东西有引用,它们有不同的生命周期
后者在这里不起作用——解释起来很复杂,但最终你会得到类似的结果
#[derive(Debug)]
pub enum Expr<'e, 'd> {
Constant(f32),
Add(&'e Expr<'e, 'd>, &'e Expr<'e, 'd>),
}
如果希望表达式树拥有大数据以及对子树的引用,则可以按照注释中的建议使用Rc
。每当克隆数据时,只有引用计数器递增,因此不会制作大副本:
use std::ops::Add;
use std::rc::Rc;
type LargeMatrix = Vec<u8>;
#[derive(Clone, Debug)]
pub enum Expr {
Constant(Rc<LargeMatrix>),
Add(Rc<Expr>, Rc<Expr>),
}
impl Add for Expr {
type Output = Expr;
fn add(self, other: Expr) -> Expr {
Expr::Add(Rc::new(self), Rc::new(other))
}
}
fn main() {
let a = Expr::Constant(Rc::new(LargeMatrix::new()));
let f = (a.clone() + a.clone()) + a;
println!("f: {:#?}", f);
}
使用std::ops::Add;
使用std::rc::rc;
类型LargeMatrix=Vec;
#[派生(克隆、调试)]
发布枚举表达式{
常数(Rc),
加上(Rc,Rc),,
}
Expr的impl Add{
类型输出=Expr;
fn添加(自身,其他:Expr)->Expr{
Expr::Add(Rc::new(self),Rc::new(other))
}
}
fn main(){
设a=Expr::Constant(Rc::new(LargeMatrix::new());
设f=(a.clone()+a.clone())+a;
println!(“f:{:?}”,f);
}
然后,您可以选择为引用实现添加:
impl<'a> Add for &'a Expr {
type Output = Expr;
fn add(self, other: &'a Expr) -> Expr {
Expr::Add(Rc::new(self.clone()), Rc::new(other.clone()))
}
}
impl感谢大家的帮助和建议。我没有意识到克隆Rc
并不能克隆内部值。因此,我可以按如下方式重新实现我的代码:
use std::ops::Add;
use std::rc::Rc;
#[derive(Debug)]
pub enum Expr {
Constant(f32),
// ^^^--this is a simplified version. In the real version,
// Constant holds large matrices and I would like to
// avoid copying it.
Add(Node, Node), // USED TO BE ADD(&NODE, &NODE)
}
#[derive(Debug, Clone)] // CLONE IS NEW
pub struct Node {
pub body: Rc<Expr> // CHANGED TO RC
}
// THIS IS NEW
impl Add for Node {
type Output = Node;
fn add(self, other: Node) -> Node { &self + &other }
}
impl<'a> Add for &'a Node {
type Output = Node;
fn add(self, other: &Node) -> Node {
Node {
body: Rc::new(Expr::Add(self.clone(), other.clone()))
}
}
}
fn main() {
let a = Node { body: Rc::new(Expr::Constant(1.)) };
let f: Node = &a + &a + a; // <-- basically, the desired outcome
println!("f: {:#?}", f);
}
使用std::ops::Add;
使用std::rc::rc;
#[导出(调试)]
发布枚举表达式{
常数(f32),
//^^^——这是一个简化版本。在真实版本中,
//常数包含大型矩阵,我想
//避免复制它。
Add(Node,Node),//用于Add(&Node,&Node)
}
#[派生(调试,克隆)]//克隆是新的
发布结构节点{
发布正文:Rc//已更改为Rc
}
//这是新的
节点的impl Add{
类型输出=节点;
fn add(self,other:Node)->Node{&self+&other}
}
impl节点{
节点{
正文:Rc::new(Expr::Add(self.clone(),other.clone())
}
}
}
fn main(){
设a=Node{body:Rc::new(Expr::Constant(1.))};
让f:Node=&a+&a+a;//为什么不按照错误消息告诉您的那样做并使用let
绑定?让f=&a+&a;让f2=&f+&a;
。为什么首先要存储对节点的引用,而不是直接存储它们?为什么不为非引用实现添加
?谢谢您的建议ion@Shepmaster。因此,我目前使用的是f1=…;f2=…
。在处理大型复杂方程时,这会使代码难以阅读。存储引用的原因是,使用add作为非引用将需要一个副本,这对性能是有问题的(我对代码做了一些简化,但在实际版本中,图形节点可以包含大型矩阵)。使用Rc
时出现了什么问题?我无法用Rc重载运算符。我用更多细节编辑了原始帖子。@ethabrooksRc
实现了Deref
和Clone
,因此使用Rc
你应该能够编写&foo+&bar
或foo.Clone()+bar.Clone()
,具体取决于您是实施添加
以供参考还是添加值。
use std::ops::Add;
use std::rc::Rc;
#[derive(Debug)]
pub enum Expr {
Constant(f32),
// ^^^--this is a simplified version. In the real version,
// Constant holds large matrices and I would like to
// avoid copying it.
Add(Node, Node), // USED TO BE ADD(&NODE, &NODE)
}
#[derive(Debug, Clone)] // CLONE IS NEW
pub struct Node {
pub body: Rc<Expr> // CHANGED TO RC
}
// THIS IS NEW
impl Add for Node {
type Output = Node;
fn add(self, other: Node) -> Node { &self + &other }
}
impl<'a> Add for &'a Node {
type Output = Node;
fn add(self, other: &Node) -> Node {
Node {
body: Rc::new(Expr::Add(self.clone(), other.clone()))
}
}
}
fn main() {
let a = Node { body: Rc::new(Expr::Constant(1.)) };
let f: Node = &a + &a + a; // <-- basically, the desired outcome
println!("f: {:#?}", f);
}