Reputation: 325
I am very new to Rust (just a couple of days). Of course, I am already getting stuck with the concept of ownership.
I have a rather lengthy problem, so here are all the relevant declarations I used:
pub struct ThePrimaryStruct<'a> {
frames: Vec<Frame<'a>>,
stack: Vec<Object<'a>>,
ip: usize
}
pub struct FunctionObject<'a> {
pub min_arity: i32,
pub max_arity: i32,
pub chunk: Chunk<'a>,
pub name: &'a str,
}
pub struct Frame<'a> {
pub function: FunctionObject<'a>,
// ... other struct members
}
pub struct Chunk<'a> {
pub codes: Vec<OpCode>, // OpCode is an enum
pub locations: Vec<(i64, i64)>,
pub constants: Vec<Object<'a>>
}
pub enum Object<'a> {
Function(FunctionObject<'a>),
// Other enum variants
}
The above code is not the problem. The problem arises when I implement the following method for ThePrimaryStruct
:
pub(crate) fn the_function(&mut self, source: &'a str) -> SomeResult {
// `Compiler::compile()` returns a FunctionObject struct.
let func: FunctionObject = Compiler::compile(source);
// The enum variant `Object::Function` takes ownership of `func`
// The code works fine up until this point.
self.stack.push(Object::Function(func));
self.frames.push(Frame {
// The struct member `function` should have the
// exact same value as the one we just pushed into
// the `self.stack` vector.
function: func, // <---- Source of conflict
// ... other struct members
});
self.run() // self.run() returns `SomeResult`
}
Running this results in the error:
error[E0382]: use of moved value: `func`
|
37 | let func: FunctionObject = Compiler::compile(source);
| ---- move occurs because `func` has type `FunctionObject<'_>`, which does not implement the `Copy` trait
...
40 | self.stack.push(Object::Function(func));
| ---- value moved here
...
44 | function: func,
| ^^^^ value used here after move
I understand why this error occurs (or at least I think I understand): The Object::Function
variant takes ownership of func
, which is then dropped from memory when we are done pushing the object into self.stack
. This then creates a conflict with the initialization of the Frame
struct because I am trying to use a value that no longer exists.
I have tried implementing the Copy
trait for the struct FunctionObject
, but that creates even more problems because FunctionObject
has a member of type Chunk<'a>
which itself has vector members.
EDIT:
Cloning definitely solves the problem. However, by cloning the FunctionObject
I would be duplicating the data inside the chunk
which could be of an arbitrarily long size. Referencing the FunctionObject
in both the stack
with Object::Function(&func)
and in the frame
with Frame { function: &func, ... }
then results in a 'func' does not live long enough
error.
Is there something fundamentally wrong with what I am trying to do?
Upvotes: 1
Views: 512
Reputation: 2507
You seem to have a FunctionObject
that you want to store both in stack
and frame
.
A better solution might be to not store the FunctionObject
directly on the stack and frame, but use smart pointers instead.
I will show you a solution using Rc
, however, you seem to be making a compiler, so you might want a different data structure to store your function objects in, like an arena.
The following code includes only the changes I made to your original, but you can also have a look on the playground where I got it to compile:
pub struct Frame<'a> {
pub function: std::rc::Rc<FunctionObject<'a>>,
// ... other struct members
}
pub enum Object<'a> {
Function(std::rc::Rc<FunctionObject<'a>>),
// Other enum variants
}
pub(crate) fn the_function(&mut self, source: &'a str) -> SomeResult {
// `Compiler::compile()` returns a FunctionObject struct.
let func: FunctionObject = Compiler::compile(source);
let func = std::rc::Rc::new(func);
// The enum variant `Object::Function` takes ownership of `func`
// The code works fine up until this point.
self.stack.push(Object::Function(func.clone()));
self.frames.push(Frame {
// The struct member `function` should have the
// exact same value as the one we just pushed into
// the `self.stack` vector.
function: func, // <---- No longer source of conflict
// ... other struct members
});
self.run() // self.run() returns `SomeResult`
}
Rc
manages an object by reference counting.
When the last Rc
is dropped, the value (FunctionObject
) will also be dropped.
Using .clone()
on func
makes a copy of the reference and increments the count, but doesn't copy the underlying object.
Upvotes: 1