Petgraph implements all its traits for references to it's internal graph type. How can I store and use a Graph
rather than an &Graph
in a struct?
This works but stores a reference to the graph:
extern crate petgraph;
use crate::petgraph::visit::*;
use petgraph::data::*;
use petgraph::*;
struct StateMachine<G>
where
G: GraphBase,
{
state_network: G,
state: <G as GraphBase>::NodeId,
}
impl<G> StateMachine<G>
where
G: IntoNodeReferences
+ IntoEdgeReferences
+ IntoEdges
+ Data
+ NodeIndexable
+ GraphProp
+ DataMap
+ GraphBase,
<G as Data>::NodeWeight: Eq + Copy,
<G as Data>::EdgeWeight: Eq + Copy,
{
pub fn next(&mut self, input: <G as Data>::EdgeWeight) -> Option<<G as Data>::NodeWeight> {
for edge in self.state_network.edges(self.state) {
if *edge.weight() == input {
self.state = edge.target();
return match self.state_network.node_weight(self.state) {
Option::Some(weight) => Some(*weight),
Option::None => Option::None,
};
}
}
return Option::None;
}
pub fn new(network: G, start: <G as Data>::NodeWeight) -> Option<StateMachine<G>> {
for nr in network.node_references() {
if *(network.node_weight(nr.id())).unwrap() == start {
return Option::Some(StateMachine {
state_network: network,
state: nr.id(),
});
}
}
return Option::None;
}
}
fn main() {
let mut sn: Graph<&str, u32, petgraph::Directed> = Graph::new();
let sn_item1 = sn.add_node("a");
let sn_item2 = sn.add_node("b");
let sn_item3 = sn.add_node("c");
let sn_item4 = sn.add_node("d");
let sn_item5 = sn.add_node("e");
sn.add_edge(sn_item1, sn_item2, 1);
sn.add_edge(sn_item1, sn_item3, 2);
sn.add_edge(sn_item2, sn_item4, 1);
sn.add_edge(sn_item2, sn_item5, 2);
sn.add_edge(sn_item5, sn_item1, 2);
sn.add_edge(sn_item5, sn_item3, 1);
let mut sm = StateMachine::new(&sn, "a").unwrap();
println!("{}", sm.next(1).unwrap());
}
But storing the Graph directly doesn't work
extern crate petgraph;
use crate::petgraph::visit::*;
use petgraph::data::*;
use petgraph::*;
struct StateMachine<'a, G>
where
&'a G: GraphBase,
{
state_network: G,
state: <&'a G as GraphBase>::NodeId,
}
impl<'a, G> StateMachine<'a, G>
where
&'a G: IntoNodeReferences
+ IntoEdgeReferences
+ IntoEdges
+ Data
+ NodeIndexable
+ GraphProp
+ DataMap
+ GraphBase,
<&'a G as Data>::NodeWeight: Eq + Copy,
<&'a G as Data>::EdgeWeight: Eq + Copy,
{
pub fn next(&mut self, input: <&'a G as Data>::EdgeWeight) -> Option<<&'a G as Data>::NodeWeight> {
for edge in (&self.state_network).edges(self.state) {
if *edge.weight() == input {
self.state = edge.target();
return match (&self.state_network).node_weight(self.state) {
Option::Some(weight) => Some(*weight),
Option::None => Option::None,
};
}
}
return Option::None;
}
pub fn new(network: G, start: <&'a G as Data>::NodeWeight) -> Option<StateMachine<'a, G>> {
for nr in network.node_references() {
if *((&network).node_weight(nr.id())).unwrap() == start {
return Option::Some(StateMachine {
state_network: network,
state: nr.id(),
});
}
}
return Option::None;
}
}
fn main() {
let mut sn: Graph<&str, u32, petgraph::Directed> = Graph::new();
let sn_item1 = sn.add_node("a");
let sn_item2 = sn.add_node("b");
let sn_item3 = sn.add_node("c");
let sn_item4 = sn.add_node("d");
let sn_item5 = sn.add_node("e");
sn.add_edge(sn_item1, sn_item2, 1);
sn.add_edge(sn_item1, sn_item3, 2);
sn.add_edge(sn_item2, sn_item4, 1);
sn.add_edge(sn_item2, sn_item5, 2);
sn.add_edge(sn_item5, sn_item1, 2);
sn.add_edge(sn_item5, sn_item3, 1);
let mut sm = StateMachine::new(sn, "a").unwrap();
println!("{}", sm.next(1).unwrap());
}
Compiling playground v0.0.1 (/playground)
error[E0495]: cannot infer an appropriate lifetime for borrow expression due to conflicting requirements
--> src/main.rs:29:21
|
29 | for edge in (&self.state_network).edges(self.state) {
| ^^^^^^^^^^^^^^^^^^^^^
|
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 28:5...
--> src/main.rs:28:5
|
28 | / pub fn next(&mut self, input: <&'a G as Data>::EdgeWeight) -> Option<<&'a G as Data>::NodeWeight> {
29 | | for edge in (&self.state_network).edges(self.state) {
30 | | if *edge.weight() == input {
31 | | self.state = edge.target();
... |
38 | | return Option::None;
39 | | }
| |_____^
note: ...so that reference does not outlive borrowed content
--> src/main.rs:29:21
|
29 | for edge in (&self.state_network).edges(self.state) {
| ^^^^^^^^^^^^^^^^^^^^^
note: but, the lifetime must be valid for the lifetime `'a` as defined on the impl at 15:6...
--> src/main.rs:15:6
|
15 | impl<'a, G> StateMachine<'a, G>
| ^^
= note: ...so that the expression is assignable:
expected <&G as petgraph::visit::GraphBase>::NodeId
found <&'a G as petgraph::visit::GraphBase>::NodeId
error[E0495]: cannot infer an appropriate lifetime for borrow expression due to conflicting requirements
--> src/main.rs:32:30
|
32 | return match (&self.state_network).node_weight(self.state) {
| ^^^^^^^^^^^^^^^^^^^^^
|
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 28:5...
--> src/main.rs:28:5
|
28 | / pub fn next(&mut self, input: <&'a G as Data>::EdgeWeight) -> Option<<&'a G as Data>::NodeWeight> {
29 | | for edge in (&self.state_network).edges(self.state) {
30 | | if *edge.weight() == input {
31 | | self.state = edge.target();
... |
38 | | return Option::None;
39 | | }
| |_____^
note: ...so that reference does not outlive borrowed content
--> src/main.rs:32:30
|
32 | return match (&self.state_network).node_weight(self.state) {
| ^^^^^^^^^^^^^^^^^^^^^
note: but, the lifetime must be valid for the lifetime `'a` as defined on the impl at 15:6...
--> src/main.rs:15:6
|
15 | impl<'a, G> StateMachine<'a, G>
| ^^
= note: ...so that the expression is assignable:
expected <&G as petgraph::visit::GraphBase>::NodeId
found <&'a G as petgraph::visit::GraphBase>::NodeId
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0495`.
error: could not compile `playground`.
To learn more, run the command again with --verbose.
I am not familiar with petgraph
and its concepts now. From my experience storing references in structs almost always ends with complete rewrites so my approach would have been something like this:
struct StateMachine<G, E, N>
where
G: GraphBase<EdgeId = E, NodeId = N>,
E: Copy + PartialEq,
N: Copy + PartialEq,
{
state_network: G,
state: N,
}
impl<G, E, N, EW, NW> StateMachine<G, E, N>
where
G: Data<NodeWeight = NW, EdgeWeight = EW>
+ NodeIndexable
+ GraphProp
+ DataMap
+ GraphBase<EdgeId = E, NodeId = N>,
E: Copy + PartialEq,
N: Copy + PartialEq,
for<'a> &'a G: IntoNodeReferences
+ IntoEdgeReferences
+ IntoEdges
+ Data<NodeWeight = NW, EdgeWeight = EW>
+ GraphBase<EdgeId = E, NodeId = N>,
EW: Eq + Copy,
NW: Eq + Copy,
{
pub fn next<'a, 'b: 'a>(&'a mut self, input: EW) -> Option<NW> {
for edge in (&self.state_network).edges(self.state) {
if *edge.weight() == input {
self.state = edge.target();
return match self.state_network.node_weight(self.state) {
Option::Some(weight) => Some(*weight),
Option::None => Option::None,
};
}
}
return Option::None;
}
pub fn new(
network: G,
start: NW,
) -> Option<StateMachine<G, <G as GraphBase>::EdgeId, <G as GraphBase>::NodeId>> {
let mut found = Option::None;
for nr in network.node_references() {
if *(network.node_weight(nr.id())).unwrap() == start {
found = Option::Some(nr.id());
}
}
found.map(|id| StateMachine {
state_network: network,
state: id,
})
}
}
EdgeId
and NodeId
get generic parameters (E
,N
)since these are reused in the implementation of StateMachine
, NodeWeight
and EdgeWeight
also get generic (NW
, EW
) but only to join the associated types of &Graph
and Graph
.