【问题标题】:Rust: Passing borrowed struct to a borrowed enum?Rust:将借来的结构传递给借来的枚举?
【发布时间】:2019-09-08 10:20:31
【问题描述】:

我正在尝试将借用的结构传递给借用的枚举。

#[derive(Copy, Clone)]
pub struct CustomerData {
    // Many fields about customers
}

#[derive(Copy, Clone)]
pub struct EmployeeData {
    // Many fields about employees
}

pub enum Person {
    Customer(CustomerData),
    Employee(EmployeeData)
}

fn do_something_with_customer(customer: &CustomerData) {
    let person = &Person::Customer(customer);

    // This would work, but this can be a large struct.
    // let person = &Person::Customer(customer.clone());

    general_method(person);
}

fn do_something_with_employee(employee: &EmployeeData) {
    let person = &Person::Employee(employee);

    // This would work, but this can be a large struct.
    // let person = &Person::Employee(employee.clone());

    general_method(person);
}

fn general_method(person: &Person) {

}

fn main() {
    let person = Person::Customer(CustomerData { });

    match &person {
        Person::Customer(data) => {
            do_something_with_customer(data);
        }
        Person::Employee(data) => {
            do_something_with_employee(data);
        }
    }
}

编译给了我结果:

error[E0308]: mismatched types
  --> src/main.rs:19:36
   |
19 |     let person = &Person::Customer(customer);
   |                                    ^^^^^^^^
   |                                    |
   |                                    expected struct `CustomerData`, found reference
   |                                    help: consider dereferencing the borrow: `*customer`
   |
   = note: expected type `CustomerData`
              found type `&CustomerData`

error[E0308]: mismatched types
  --> src/main.rs:28:36
   |
28 |     let person = &Person::Employee(employee);
   |                                    ^^^^^^^^
   |                                    |
   |                                    expected struct `EmployeeData`, found reference
   |                                    help: consider dereferencing the borrow: `*employee`
   |
   = note: expected type `EmployeeData`
              found type `&EmployeeData`

我知道 Rust 编译器不允许我这样做,但我觉得我应该能够这样做,考虑到我传递我的结构的枚举也是借用的。

这种情况有模式/解决方法吗?也许使用Rc 类型?我不想在这种情况下乱扔我的代码。

use std::rc::Rc;

#[derive(Copy, Clone)]
pub struct CustomerData {
    // Many fields about customers
}

#[derive(Copy, Clone)]
pub struct EmployeeData {
    // Many fields about employees
}

pub enum Person {
    Customer(Rc<CustomerData>),
    Employee(Rc<EmployeeData>)
}

fn do_something_with_customer(customer: Rc<CustomerData>) {
    let person = &Person::Customer(customer);

    // This would work, but this can be a large struct.
    // let person = &Person::Customer(customer.clone());

    general_method(person);
}

fn do_something_with_employee(employee: Rc<EmployeeData>) {
    let person = &Person::Employee(employee);

    // This would work, but this can be a large struct.
    // let person = &Person::Employee(employee.clone());

    general_method(person);
}

fn general_method(person: &Person) {

}

fn main() {
    let person = Person::Customer(Rc::new(CustomerData { }));

    match &person {
        Person::Customer(data) => {
            do_something_with_customer(data.clone());
        }
        Person::Employee(data) => {
            do_something_with_employee(data.clone());
        }
    }
}

【问题讨论】:

    标签: enums rust borrow-checker


    【解决方案1】:

    您错误地识别了问题,并且编译器发现了它的错误 cmets。

    你这样定义你的枚举:

    pub enum Person {
        Customer(CustomerData),
        Employee(EmployeeData)
    }
    

    然后你决定你的枚举成员应该是Person::Customer(&amp;CustomerData)

    fn do_something_with_customer(customer: &CustomerData) {
        let person = &Person::Customer(customer);
    

    引用不可传递。因为&amp;CustomerData 是引用并不意味着整个枚举都是对真实数据的引用(即&amp;Person::Customer(CustomerData))。

    有两种方法可以修复它;显而易见的是查看CustomerData 是否实现Copy。如果是这样,您可以取消引用(因此隐式复制):

    fn do_something_with_customer(customer: &CustomerData) {
        let person = Person::Customer(*customer);
    

    (这是编译器建议的,所以我很确定你的类型实现了Copy

    另一个选项是在类型上#[derive(Clone)] 并调用customer.clone()。同样,以额外分配为代价。

    如果你真的想要枚举中的引用,你需要将枚举定义更改为:

    pub enum Person<'a> {
        Customer(&'a CustomerData),
        Employee(&'a EmployeeData)
    }
    

    并处理对象属性现在是引用的事实,以及相关的所有问题。

    【讨论】:

    • 这不是关于this 的安全性,而是关于之后会发生什么 的安全性。如果复制结构(通过RcCopyClone),您实际上在内存中有两个独立的实体(Rc 是一种特殊情况),它们都是不可变的。如果你想改变这些数据怎么办?
    • CopyClone 的情况下,您可以,但您不会触及其他结构。对于Rc,您不能,句号,然后您需要将这个Rc 包裹在RefCell 周围以具有内部可变性
    • 关键在于你真正想要达到的整体目标。即使拥有枚举,您是否只想要数据的不可变副本? Rc。你想传递一个引用并保证你的对象生命周期限制合适吗? &amp;。你的案子是flyweight pattern吗? Copy/Clone 很好。
    • 此外,没有真正的理由害怕引用。如果您以理智的方式编写代码,您只会在结构、特征和枚举的定义中真正看到它们的定义;在实际的业务逻辑中,它们中的大多数都被编译器省略和/或简化了。真正的症结在于,有三种解决方案可以解决您的问题,它们都取决于您在 您向我们展示的 sn-p 之后如何处理数据 :-)
    • 这排除了 99% 的情况下的引用。既然您提到自己 Clone/Copy 由于大小而不是一个选项,这意味着您用 Rc 回答了您自己的问题 :-) 请注意,Rc::new() 调用实际上并不是必需的。 Rc&lt;T&gt; 实现了From&lt;T&gt;,这意味着无论您在哪里需要Rc&lt;T&gt;,您都可以简单地调用into()
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-07-23
    • 2022-12-10
    • 1970-01-01
    • 1970-01-01
    • 2020-10-19
    • 2019-12-09
    • 2021-07-25
    相关资源
    最近更新 更多