Rust noob here. How can I populate a vector of references to a trait with variables defined locally?
I tried something like this (playground):
trait Trait {
fn as_trait(&self) -> &dyn Trait;
}
struct Struct1 {
value: i32,
}
impl Trait for Struct1 {
fn as_trait(&self) -> &dyn Trait {
self
}
}
struct Collection {
values: Vec<&'static dyn Trait>,
}
fn populate(coll: &mut Collection) {
for i in 0..10 {
let foo = Struct1 {value: i};
coll.values.push(foo.as_trait());
}
}
fn main() {
let mut coll = Collection { values: vec![] };
populate(&mut coll);
}
This obviously returns the following error:
error[E0597]: `foo` does not live long enough
--> src/main.rs:21:26
|
21 | coll.values.push(foo.as_trait());
| ^^^-----------
| |
| borrowed value does not live long enough
| cast requires that `foo` is borrowed for `'static`
22 | }
23 | }
| - `foo` dropped here while still borrowed
I also tried to use lazy_static macro without success (playground):
use lazy_static::lazy_static;
trait Trait {
fn as_trait(&self) -> &dyn Trait;
}
struct Struct1 {
value: i32,
}
impl Trait for Struct1 {
fn as_trait(&self) -> &dyn Trait {
self
}
}
struct Collection {
values: Vec<&'static dyn Trait>,
}
fn populate(coll: &mut Collection) {
for i in 0..10 {
lazy_static! {
static ref foo: Struct1 = Struct1 {value: i};
}
coll.values.push(foo.as_trait());
}
}
fn main() {
let mut coll = Collection { values: vec![] };
populate(&mut coll);
}
In this case the error is the following:
error[E0434]: can't capture dynamic environment in a fn item
--> src/main.rs:23:55
|
23 | static ref foo: Struct1 = Struct1 {value: i};
| ^
|
= help: use the `|| { ... }` closure form instead
Related
I'm having a hard time figuring out the lifetime syntax that I need for the following code, which does not compile. The basic idea is that I'm creating an Abstract Syntax Tree, and each node must have a different type. Some node types must hold a reference to an external value. It's not possible to make this external value an owned type; it's actually a reference to disk file that must be shared by a number of nodes. Here's the code, as simple as I can make it:
trait Node {
fn init(&mut self, my_str: &str);
}
struct NodeParent {
pub children: Vec<Box<dyn Node>>,
}
impl Node for NodeParent {
fn init(&mut self, my_str: &str) {
for child in self.children.iter_mut() {
child.init(my_str);
}
}
}
struct NodeLeaf<'a> {
pub my_str: Option<&'a str>,
}
impl Node for NodeLeaf<'_> {
fn init(&mut self, my_str: &str) {
self.my_str = Some(my_str);
}
}
impl NodeLeaf<'_> {
pub fn new() -> Box<dyn Node> {
Box::new(NodeLeaf { my_str: None })
}
}
pub fn make_ast() {
let mut parent = NodeParent { children: vec![] };
let leaf = NodeLeaf::new();
parent.children.push(leaf);
let some_string = String::from("foo");
let my_str = some_string.as_str();
parent.init(my_str);
}
The error is:
error: lifetime may not live long enough
--> src/query/lifetime_test.rs:23:9
|
22 | fn init(&mut self, my_str: &str) {
| --------- - let's call the lifetime of this reference `'1`
| |
| has type `&mut NodeLeaf<'2>`
23 | self.my_str = Some(my_str);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ assignment requires that `'1` must outlive `'2`
I know I need some kind of lifetime on &my_str everywhere it appears, but once I start taking the compiler's suggestion to start adding lifetimes here and there they proliferate, and I never get to code that compiles. I've also tried to Rc<>, but the code to make that work eludes me as well.
How do I specify that &my_str outlives the whole Node tree?
Link to the code on Rust Playground
Add a lifetime to Node:
trait Node<'a> {
fn init(&mut self, my_str: &'a str);
}
struct NodeParent<'a> {
pub children: Vec<Box<dyn Node<'a>>>,
}
impl<'a> Node<'a> for NodeParent<'a> {
fn init(&mut self, my_str: &'a str) {
for child in self.children.iter_mut() {
child.init(my_str);
}
}
}
struct NodeLeaf<'a> {
pub my_str: Option<&'a str>,
}
impl<'a> Node<'a> for NodeLeaf<'a> {
fn init(&mut self, my_str: &'a str) {
self.my_str = Some(my_str);
}
}
impl<'a> NodeLeaf<'a> {
pub fn new() -> Box<dyn Node<'a>> {
Box::new(NodeLeaf { my_str: None })
}
}
But this is not enough, because Box<dyn Node<'a>> is actually Box<dyn Node<'a> + 'static>, that is, it cannot contain any non-'static lifetime. You need to specify that it can contain lifetime 'a:
struct NodeParent<'a> {
pub children: Vec<Box<dyn Node<'a> + 'a>>,
}
impl<'a> NodeLeaf<'a> {
pub fn new() -> Box<dyn Node<'a> + 'a> {
Box::new(NodeLeaf { my_str: None })
}
}
Now you will get a different error:
error[E0597]: `some_string` does not live long enough
--> src/lib.rs:39:18
|
39 | let my_str = some_string.as_str();
| ^^^^^^^^^^^^^^^^^^^^ borrowed value does not live long enough
40 | parent.init(my_str);
41 | }
| -
| |
| `some_string` dropped here while still borrowed
| borrow might be used here, when `parent` is dropped and runs the destructor for type `NodeParent<'_>`
|
= note: values in a scope are dropped in the opposite order they are defined
Because Box<dyn Node> may have an arbitrary destructor, and it may access the stored string, we need to make sure it is still alive when the value is dropped. Do that by moving the string creation before the nodes:
pub fn make_ast() {
let some_string = String::from("foo");
let mut parent = NodeParent { children: vec![] };
let leaf = NodeLeaf::new();
parent.children.push(leaf);
let my_str = some_string.as_str();
parent.init(my_str);
}
Playground.
Here is a small code sample representing the problem I currently have. CmdMap stores some logic from Trait types that should be triggered by a String key. This logic requires a context that is provided with the Arg instance. In this Arg instance, we may need mutable references to some objects (represented here by Printer).
Here's the code:
use std::collections::HashMap;
struct Cmd {}
pub trait Trait<T> {
fn execute(args: T) -> Result<(), String>;
}
impl<'a> Trait<Arg<'a>> for Cmd {
fn execute(arg: Arg) -> Result<(), String> {
arg.printer.print("execute".to_string());
Ok(())
}
}
struct Printer {
name: String,
}
impl Printer {
pub fn print(&mut self, msg: String) {
self.name = "print".to_string();
println!("{}: {}", self.name, msg);
}
}
struct Arg<'a> {
pub printer: &'a mut Printer,
}
type Callback<T> = dyn Fn(T) -> Result<(), String>;
struct CmdMap<T> {
pub map: HashMap<String, Box<Callback<T>>>,
}
impl<T> CmdMap<T> {
pub fn try_execute(&self, name: String, arg: T) {
self.map[&name](arg);
}
}
fn test() {
let mut map: CmdMap<Arg> = CmdMap {
map: HashMap::new(),
};
map.map.insert("test".to_string(), Box::new(Cmd::execute));
let mut printer: Printer = Printer {
name: "".to_string(),
};
loop {
map.try_execute(
"test".to_string(),
Arg {
printer: &mut printer,
},
);
}
}
The compiler complains:
error[E0499]: cannot borrow `printer` as mutable more than once at a time
--> src/lib.rs:57:26
|
47 | map.map.insert("test".to_string(), Box::new(Cmd::execute));
| ---------------------- cast requires that `printer` is borrowed for `'static`
...
57 | printer: &mut printer,
| ^^^^^^^^^^^^ mutable borrow starts here in previous iteration of loop
...
58 | map.try_execute("test".to_string(), Arg { printer: &mut printer });
| ^^^^^^^^^^^^ mutable borrow starts here in previous iteration of loop
I don't undersand why the compiler complains here. The first error cast requires that printer is borrowed for 'static, doesn't make sense to me as I'm storing Fn types that need to borrow printer for the time of their execution only.
Am I missing something?
I have a trait with a function that takes a reference to an iterator:
#[derive(Clone)]
struct Dog {
name: &'static str,
}
trait DogListAction<'a, I>
where
I: Iterator<Item = &'a Dog>,
{
fn on_dog_list(&mut self, dog_list: I);
}
struct DogListActionExample {}
impl<'a, I> DogListAction<'a, I> for DogListActionExample
where
I: Iterator<Item = &'a Dog>,
{
fn on_dog_list(&mut self, dog_list: I) {
for dog in dog_list {
println!("{}", dog.name);
}
}
}
fn main() {
let dogs = vec![Dog { name: "Pluto" }, Dog { name: "Lilly" }];
let mut action_example = DogListActionExample {};
let mut dog_list_actions: Vec<Box<DogListAction<_>>> = vec![Box::new(action_example)];
loop {
let dog_clone = dogs.clone();
for dog_list_action in &mut dog_list_actions {
dog_list_action.on_dog_list(dog_clone.iter());
}
}
}
playground
It does not take any reference to the elements, so there is no need for it to last for more time than the function call.
Due to my limited understanding of lifetimes, I do not know yet how to express this. Calling this function causes a compilation error:
error[E0597]: `dog_clone` does not live long enough
--> src/main.rs:33:41
|
33 | dog_list_action.on_dog_list(dog_clone.iter());
| ^^^^^^^^^ borrowed value does not live long enough
34 | }
35 | }
| - `dog_clone` dropped here while still borrowed
36 | }
| - borrowed value needs to live until here
I guess the borrow checker thinks that data in dog_clone may be referenced after the function ends, but this is not the case.
The problem here is that the code can potentially save short-lived references to dog_clone elements in the longer-lived dog_list_actions. We need to tell the compiler that we will not be saving references the iterator produces. It can be done like this:
trait DogListAction {
fn on_dog_list<'a, I>(&'a mut self, dog_list: I)
where
I: Iterator<Item = &'a Dog>;
}
Now Item's can live for the duration of on_dog_list call. In the original code they must live for longer duration.
But this code creates another problem: we can't box the trait DogListAction anymore as it includes generic function. The usual approach here is to use a trait object:
trait DogListAction {
fn on_dog_list<'a>(&'a mut self, dog_list: Box<dyn Iterator<Item = &'a Dog> + 'a>);
}
Note the second 'a in Box<dyn Iterator<Item = &'a Dog> + 'a>. Rust adds a 'static trait bound to boxed trait objects by default and we don't want 'static here.
#[derive(Clone)]
struct Dog {
name: &'static str,
}
trait DogListAction {
fn on_dog_list<'a>(&'a mut self, dog_list: Box<dyn Iterator<Item = &'a Dog> + 'a>);
}
struct DogListActionExample {}
impl DogListAction for DogListActionExample {
fn on_dog_list<'a>(&'a mut self, dog_list: Box<dyn Iterator<Item = &'a Dog> + 'a>) {
for dog in dog_list {
println!("{}", dog.name);
}
}
}
fn main() {
let dogs = vec![Dog { name: "Pluto" }, Dog { name: "Lilly" }];
let action_example = DogListActionExample {};
let mut dog_list_actions: Vec<Box<DogListAction>> = vec![Box::new(action_example)];
{
let dogs_clone = dogs.clone();
for dog_list_action in &mut dog_list_actions {
dog_list_action.on_dog_list(Box::new(dogs_clone.iter()));
}
}
}
Playground
I'd like to modify data in a struct based on a trait that is boxed. The following code prints the value but gives me "cannot mutably borrow immutable field" when I try to change it or "cannot borrow as mutable" when calling its function.
My plan is to have a vector of Ai each containing the AiData derived struct and then iterate over them, set some data in it and call the tick() function.
use std::any::Any;
pub trait AiData {
fn tick(&mut self);
fn as_any(&self) -> &Any;
}
pub struct Ai {
pub ai_data: Box<AiData>,
}
impl Ai {
pub fn new(ai_data: Box<AiData>) -> Ai {
Ai { ai_data: ai_data }
}
}
pub struct TestAi {
pub index: u8,
}
impl TestAi {
pub fn new() -> TestAi {
TestAi { index: 1 }
}
}
impl AiData for TestAi {
fn tick(&mut self) {
println!("tick");
}
fn as_any(&self) -> &Any {
self
}
}
fn main() {
let ai_data: TestAi = TestAi::new();
let ai: Ai = Ai::new(Box::new(ai_data));
let b: &TestAi = match ai.ai_data.as_any().downcast_ref::<TestAi>() {
Some(b) => b,
None => panic!("&a isn't a B!"),
};
println!("{:?}", b.index);
b.tick();
b.index = 2;
}
error[E0596]: cannot borrow immutable borrowed content `*b` as mutable
--> src/main.rs:48:5
|
48 | b.tick();
| ^ cannot borrow as mutable
error[E0594]: cannot assign to immutable field `b.index`
--> src/main.rs:49:5
|
49 | b.index = 2;
| ^^^^^^^^^^^ cannot mutably borrow immutable field
How to get mutable struct from boxed trait
You cannot get a struct from the boxed trait object. You can get a reference to the struct, however.
As explained in The Rust Programming Language's chapter on variables and mutability, mutability is a property of the binding. Additionally, as described in the chapter on references and borrowing, a mutable reference (&mut T) is distinct from an immutable reference (&T). Based on these two points, you cannot get a mutable reference from an immutable variable1.
The code has:
An immutable variable
An immutable reference to that variable
Calls Any::downcast_ref, which returns an immutable reference
When you fix all of those, the code works:
use std::any::Any;
pub trait AiData {
fn tick(&mut self);
fn as_any_mut(&mut self) -> &mut Any;
}
pub struct Ai {
pub ai_data: Box<AiData>,
}
impl Ai {
pub fn new(ai_data: Box<AiData>) -> Ai {
Ai { ai_data }
}
}
pub struct TestAi {
pub index: u8,
}
impl TestAi {
pub fn new() -> TestAi {
TestAi { index: 1 }
}
}
impl AiData for TestAi {
fn tick(&mut self) {
println!("tick");
}
fn as_any_mut(&mut self) -> &mut Any {
self
}
}
fn main() {
let ai_data = TestAi::new();
let mut ai = Ai::new(Box::new(ai_data));
let b = ai.ai_data
.as_any_mut()
.downcast_mut::<TestAi>()
.expect("&a isn't a B!");
println!("{:?}", b.index);
b.tick();
b.index = 2;
}
1 You can read about interior mutability which actually does allow you to get a mutable reference from an immutable variable, at the expense of introducing runtime checks to prevent aliasing.
See also:
Rust: downcasting and Box<Any>
How to get a struct reference from a boxed trait?
I'm using the structs Foo and Bar from a library and I'm getting a compilation error in the client code. I simplified the code to this:
use std::marker::PhantomData;
struct Foo {
some_str: &'static str,
}
struct Bar<'a> {
some_str: &'static str,
marker: PhantomData<&'a Foo>,
}
impl Foo {
fn read_data(&self) {
// add code here
}
fn create_bar<'a>(&'a mut self) -> Bar<'a> {
Bar {
some_str: "test2",
marker: PhantomData,
}
}
}
fn process(_arr: &mut [Bar]) {}
fn main() {
let mut foo = Foo { some_str: "test" };
let mut array: [Bar; 1] = [foo.create_bar()];
process(&mut array);
foo.read_data();
}
(playground)
Output:
error[E0502]: cannot borrow `foo` as immutable because it is also borrowed as mutable
--> src/main.rs:30:5
|
28 | let mut array: [Bar; 1] = [foo.create_bar()];
| --- mutable borrow occurs here
29 | process(&mut array);
30 | foo.read_data();
| ^^^ immutable borrow occurs here
31 | }
| - mutable borrow ends here
The error in the console output is very clear, but I cannot fix the problem.
You can limit the lifetime of the array variable by placing it in a new scope with curly braces ({ ... }):
fn main() {
let mut foo = Foo { some_str: "test" };
{
let mut array: [Bar; 1] = [foo.create_bar()];
process(&mut array);
}
foo.read_data();
}
You original code will work as-is once non-lexical lifetimes are enabled by default:
#![feature(nll)]
use std::marker::PhantomData;
struct Foo {
some_str: &'static str,
}
struct Bar<'a> {
some_str: &'static str,
marker: PhantomData<&'a Foo>,
}
impl Foo {
fn read_data(&self) {
// add code here
}
fn create_bar<'a>(&'a mut self) -> Bar<'a> {
Bar {
some_str: "test2",
marker: PhantomData,
}
}
}
fn process(_arr: &mut [Bar]) {}
fn main() {
let mut foo = Foo { some_str: "test" };
let mut array: [Bar; 1] = [foo.create_bar()];
process(&mut array);
foo.read_data();
}
With NLL, the borrow checker becomes more advanced and precise; it can now understand that you aren't using array after the call to process so it is safe to use foo in a new manner.