Reputation: 9776
I'm trying to pass a borrowed struct into a borrowed enum.
#[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);
}
}
}
Compiling gives me the result:
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`
I get that the Rust compiler isn't allowing me to do this, but I feel like I should be able to, considering the enum I'm passing my structs to is borrowed as well.
Is there a pattern/workaround for this scenario? Maybe using the Rc
type? I'd hate to litter my code with it for this scenario.
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());
}
}
}
Upvotes: 1
Views: 740
Reputation: 19662
You have misidentified the problem and the compiler was spot-on on its error comments.
You defined your enum like so:
pub enum Person {
Customer(CustomerData),
Employee(EmployeeData)
}
But then you decide that your enum member should be Person::Customer(&CustomerData)
:
fn do_something_with_customer(customer: &CustomerData) {
let person = &Person::Customer(customer);
References are not transitive. Because &CustomerData
is a reference does not mean that the entire enum will be a reference to real data (i.e. &Person::Customer(CustomerData)
).
There are two ways to fix it; the obvious is to see if CustomerData
implements Copy
. If it does, you can just dereference (and therefore implicitly copy):
fn do_something_with_customer(customer: &CustomerData) {
let person = Person::Customer(*customer);
(This is what the compiler suggested, so I'm pretty sure your type implements Copy
)
The other option is to #[derive(Clone)]
on the type and call customer.clone()
. Again, at the cost of an extra allocation.
If you really want the reference in the enum, you need to change the enum definition to:
pub enum Person<'a> {
Customer(&'a CustomerData),
Employee(&'a EmployeeData)
}
And handle the fact that the object property is now a reference, with all the problems associated.
Upvotes: 4