I am trying to create a graph using RefCell
and Rc
. I want to create 100 nodes in a loop with string labels. This is the graph representation:
struct Node {
datum: &'static str,
edges: Vec<Rc<RefCell<Node>>>,
}
impl Node {
fn new(datum: &'static str) -> Rc<RefCell<Node>> {
Rc::new(RefCell::new(Node {
datum: datum,
edges: Vec::new(),
}))
}
}
This is the loop I am writing to create nodes:
for i in 0..100 {
let a = Node::new(concat!("A", i));
let b = Node::new(concat!("A", i + 1));
{
let mut mut_root = a.borrow_mut();
mut_root.edges.push(b.clone());
}
}
This is the error I am getting:
error: expected a literal
--> src/main.rs:17:40
|
17 | let a = Node::new(concat!("A", i));
| ^
error: expected a literal
--> src/main.rs:18:40
|
18 | let b = Node::new(concat!("A", i + 1));
| ^^^^^
You cannot.
Lifetimes are descriptive, not prescriptive: you can't create a string and say "this data has the
'static
lifetime." Rust makes you write lifetime annotations like'static
so that the compiler can check your work. In the case of building a string in a runtime loop, you can't safely call it'static
because it's not. (See also How to initialize a variable with a lifetime?)But that doesn't mean you don't have any options.
Avoiding lifetimes altogether
Possibly what you're looking for is just to avoid annotating
Node
with a lifetime parameter, likeNode<'a>
. In which case, just useString
instead:This is idiomatic and most likely the best option. If you're sure you will never need
String
's methods after creating it, and you don't want to store its capacity, you can callString::into_boxed_str
to make it aBox<str>
instead. This is the same storage-wise as&'static str
, but the heap storage for thestr
will be reclaimed when theNode
is dropped.You could even use
Rc<str>
, which is also the same storage as&'static str
but cheaper to clone than aBox
.Making non-static data
'static
by leakingIf you really want to, you can "fake" a
&'static str
by making aBox<str>
and leaking it -- i.e. discarding the knowledge that it's aBox
so that its backing memory will never be reclaimed. Since Rust 1.26, you can useBox::leak
; I'll make a helper function to create the labels:The main thing this gives you is that
&
references areCopy
. ButNode
isn'tCopy
anyway, so why not just useBox
?Generating labels at or before compile time
If you need a string that's actually
'static
, it has to be there at compile time. As far as I know,macro_rules
macros aren't powerful enough to do this, but you can always generate code. Here's one possibility:This might not seem like the most elegant solution, but it has several advantages over the above suggestions. The strings don't have to be allocated, so they're as cheap as possible. The code size is smaller. The resulting code is easier for the compiler to optimize. It's easy to generate
LABELS
with an editor macro or a one-liner in your scripting language of choice. It's also trivial to check, because it's just a list of increasing integers. The biggest reason why you would want to avoid code generation is if 100 is a number that may change and you don't want to edit multiple locations in the code when that happens. In that case runtime generation (using one of the ideas above) is probably your best bet.