Hey folks, I'm hitting a bit of a wall with this one.
Basically, I have some wrapping over reqwest
and reqwest_middleware
, and need it to be generic over the error types involved (since these libraries both return a the same Response
on success, but different error types). The strange part is that cargo check
reports no issues, but cargo build
results in a linker error claiming part of core
doesn't exist, which is surprising to me.
I've boiled the issue down to a minimum reproducing case (playground link), tearing out all dependencies and using just standard rust - it comes to about 130 lines of code, including some comments. It's a little weird looking since I tried to make the problem statement generic, rather than referencing anything specific to reqwest
or similar. Also reproduced here:
// Imagine we have two "factories", both of which can produce two different "builders".
struct FactoryA;
struct FactoryB;
struct BuilderA;
struct BuilderB;
// Imagine each builder can produce either a Vec<u8>, or some builder-specific error.
pub enum ErrorA {
A,
}
pub enum ErrorB {
B,
}
// We have some trait to represent the notion of a "builder factory"
pub trait BuilderFactory {
type Builder: BuilderInner;
fn get_builder(&self) -> Builder<Self::Builder>;
}
// And a generic wrapper around the factory (imagine we have some logic that's generic across all factories)
pub struct FactoryWrapper<T: BuilderFactory> {
pub inner: T,
}
// We also have a trait to represent the "builder"
pub trait BuilderInner: Send {
type Error;
fn build(self) -> Result<Vec<u8>, Self::Error>;
}
// And a generic wrapper around the builder
pub struct Builder<T: BuilderInner> {
inner: T,
}
// We'll implement the BuilderFactory trait for each of our factories
impl BuilderFactory for FactoryA {
type Builder = BuilderA;
fn get_builder(&self) -> Builder<Self::Builder> {
Builder { inner: BuilderA }
}
}
impl BuilderFactory for FactoryB {
type Builder = BuilderB;
fn get_builder(&self) -> Builder<Self::Builder> {
Builder { inner: BuilderB }
}
}
// And implement the wrapper around our builders - imagine there is some async work done
// in this wrapper
impl<T: BuilderInner> Builder<T> {
pub async fn build(self) -> Result<Vec<u8>, <T as BuilderInner>::Error> {
self.inner.build()
}
}
// We'll implement our builder trait for our builders
impl BuilderInner for BuilderA {
type Error = ErrorA;
fn build(self) -> Result<Vec<u8>, Self::Error> {
Ok(vec![])
}
}
impl BuilderInner for BuilderB {
type Error = ErrorB;
fn build(self) -> Result<Vec<u8>, Self::Error> {
Ok(vec![])
}
}
// Lets say we have a trait that is designed to let any implementer that can provide one of these factories
// easily "produce" something (imagine there is more to the builders, such that using them involves some steps)
// Since the builder wrapper does some async work, this must also be async
pub trait Produce<'a>: Sized + 'a {
type Builder: BuilderFactory;
fn get_factory(&self) -> &FactoryWrapper<Self::Builder>;
fn produce(
self,
) -> ::core::pin::Pin<
Box<
dyn ::core::future::Future<
Output = Result<
Vec<u8>,
<<Self::Builder as BuilderFactory>::Builder as BuilderInner>::Error,
>,
> + ::core::marker::Send
+ 'a,
>,
>
where
Self: ::core::marker::Send,
{
Box::pin(async move {
let builder = self.get_factory().inner.get_builder();
let built = builder.build().await?;
return Ok(built);
})
}
}
//Imagine we have one such "factory owner"
// We'll have some "context" that's generic across all factories
pub struct FactoryOwner<'a, Factory: BuilderFactory> {
bridge: &'a FactoryWrapper<Factory>,
}
// And we'll have some trait we want to implement for our context
impl<'a, Builder: BuilderFactory> Produce<'a> for FactoryOwner<'a, Builder> {
type Builder = Builder;
fn get_factory(&self) -> &crate::FactoryWrapper<Self::Builder> {
self.bridge
}
}
// This all type checks and passes cargo check, but cargo build produces a linker error
fn main() {
let bridge = FactoryWrapper { inner: FactoryA };
let context = FactoryOwner { bridge: &bridge };
context.produce();
}
Is this some simple mistake I'm making? And if so, should cargo check
catch it? Or should I open an issue on the cargo issue tracker?
Thanks!