Closed pnevyk closed 1 year ago
Alternative builder API:
ShortestPaths::on(&graph).run(start); // with goal = None, edge_weight = identity, algo = any
ShortestPaths::on(&graph).goal(goal).edge_weight(edge_weight).dijkstra().run(start);
The difference between on
and run
methods is:
on
would always take the graph, which would determine the graph concrete type, which would often be necessary or helpful with restricting the builder API available (e.g., bellman_ford()
could not be used if the graph does no implement required traits).run
would take required arguments of the algorithm where no default value makes sense (in the shortest paths example, it is the start of the search).The advantage is that the appearance of the usage is consistent whether or not any builder method is used. Although there could still be a shortcut:
ShortestPaths::run(&graph, start); // with goal = None, edge_weight = identity, algo = any
When implementing the run
method on the builder, specify the graph constraints on the method level rather than impl level, because it arguably gives slightly better error messages when violated (playground).
// Method level - GOOD
impl<G> Builder<G, Dijkstra> {
pub fn run(self, ...)
where
G: ...,
{
// ...
}
}
// Impl level - WORSE
impl<G> Builder<G, BellmanFord>
where
G: ...,
{
pub fn run(self, ...) {
// ...
}
}
Instead of using several
run
methods with many arguments, the algorithms can use the builder pattern with reasonable defaults instead. Example withShortestPaths
:Current way
Builder pattern
The API is just a quick, first draft and is subject to change.
An interesting challenge will be to allow to choose a specific algorithm which should also result in more specific constraints on the graph (for which there are now separate functions such as
run_dijkstra
). But this should be quite doable with type parameters and zero-sized new types.The main advantages are:
Option
) with a generic type without any hassle (for a functionfn foo<T>(x: Option<T>)
the compiler cannot infer the type forT
infoo(None)
).