That looks a bit too specific to have a general pattern name, but to me, it feels like a complex way to check whether some object implements a particular trait.
Maybe you are trying to copy a pattern from C#/Java where you check whether an Object
variable implements a particular interface and cast it to that interface if so?
interface IDataSourceBase { ... }
interface IDataSource<T>: IDataSourceBase {
T Get();
}
class Foo: IDataSourceBase, IDataSource<InstanceUrl> { ... }
class Program
{
static void Main() {
IDataSourceBase data = getDataSourceFromSomewhere();
if (data is IDataSource<InstanceUrl> urlDataSource)
{
InstanceUrl url = urlDataSource.get();
Console.WriteLine("URL is {}", url);
} else {
Console.WriteLine("Unknown URL");
}
}
}
That sort of architecture isn't very idiomatic in Rust (probably why you ran into a lot of friction), but if I had to I'd probably write it like this:
trait DataSourceBase {
fn title(&self) -> Option<dyn DataSource<InstanceTitle>> { None }
fn url(&self) -> Option<dyn DataSource<InstanceBaseUrl>> { None }
fn repolist(&self) -> Option<dyn DataSource<RepoListUrl>> { None }
}
trait DataSource<T>: DataSourceBase {
fn get(&self) -> &T;
}
You would then pass an Arc<dyn DataSourceBase>
around. If you need access to title data you call data_source.title()
and handle the case where it doesn't implement DataSource<InstanceBaseUrl>
.
struct UrlAndTitle {
url: InstanceBaseUrl,
title: InstanceTitle,
}
impl DataSourceBase for UrlAndTitle {
fn title(&self) -> Option<dyn DataSource<InstanceTitle>> { Some(self) }
fn url(&self) -> Option<dyn DataSource<InstanceBaseUrl>> { Some(self) }
// note: leave repolist() with the default implementation that returns None
}
impl DataSource<InstanceBaseUrl> for UrlAndTitle {
fn get(&self) -> InstanceBaseUrl { &self.url }
}
impl DataSource<InstanceTitle> for UrlAndTitle {
fn get(&self) -> InstanceTitle { &self.title }
}
fn main() {
let data: Arc<DataSourceBase> = get_data_source_from_somewhere();
match data.url() {
Some(url) => println!("URL is {}", url),
None => println!("Unknown URL"),
}
}
If the data source needs to mutate its fields it'll need to use locks to synchronise access, but from the context it looks like this is intended for some sort of web server so the C# implementation would also need to use locking to avoid data races.
Either way, the interior mutability is an internal detail of the UrlAndTitle
type, whereas the DataSourceBase
trait just cares about the public interface and shouldn't mention anything about QCell
or Arc
.