Same code used in factory method does not work


#1

I’m in the process of moving some code out of main into a factory method. I’ve tried to keep the code as similar as possible to try to isolate the problem I’m having.

The problem I’m having is that the factory method should return a struct that is generic over a trait bounded parameter.

In main, the code works fine and I can assign a concrete type that implements the trait to the parameterized struct field.

The factory method, on the other hand, won’t compile because the compiler won’t accept the concrete type as an implementation of the generic struct.

The good code:

https://github.com/ereichert/cargo-release/blob/feature/release_context_isolation.repo_constructor/src/main.rs#L44

The assignment of the concrete type to the generic field is at

https://github.com/ereichert/cargo-release/blob/feature/release_context_isolation.repo_constructor/src/main.rs#L49

The bad code:

https://github.com/ereichert/cargo-release/blob/feature/release_context_isolation.repo_constructor/src/release_context.rs#L21

The compilation errors that code generates:

src/release_context.rs:22:26: 27:14 error: mismatched types:
expected release_context::ReleaseContext<'_, R>,
found release_context::ReleaseContext<'_, repo::RepoImpl<'_, git2::repo::Repository>>
(expected type parameter,
found struct repo::RepoImpl) [E0308]
src/release_context.rs:22 Ok(or) => Ok(ReleaseContext {
src/release_context.rs:23 release_type: release_type,
src/release_context.rs:24 snapshot_branch_name: snapshot_branch,
src/release_context.rs:25 disable_checks: disable_checks,
src/release_context.rs:26 repo: or,
src/release_context.rs:27 }),
src/release_context.rs:22:26: 27:14 help: run rustc --explain E0308 to see a detailed explanation
error: aborting due to previous error
Build failed, waiting for other jobs to finish…
src/release_context.rs:22:26: 27:14 error: mismatched types:
expected release_context::ReleaseContext<'_, R>,
found release_context::ReleaseContext<'_, repo::RepoImpl<'_, git2::repo::Repository>>
(expected type parameter,
found struct repo::RepoImpl) [E0308]
src/release_context.rs:22 Ok(or) => Ok(ReleaseContext {
src/release_context.rs:23 release_type: release_type,
src/release_context.rs:24 snapshot_branch_name: snapshot_branch,
src/release_context.rs:25 disable_checks: disable_checks,
src/release_context.rs:26 repo: or,
src/release_context.rs:27 }),
src/release_context.rs:22:26: 27:14 help: run rustc --explain E0308 to see a detailed explanation
error: aborting due to previous error
Could not compile cargo-release.

To learn more, run the command again with --verbose.
-> exit code: 101

I’ve tried to fix this a few different ways, all of which seemed rational.

For instance, being explicit about the generic parameter.

Ok(or) => Ok(ReleaseContext::<'a, Repo<'a>> {
release_type: release_type,
snapshot_branch_name: snapshot_branch,
disable_checks: disable_checks,
repo: or,
})

But I’ve been unable to figure this out. I’m stuck on this.

Thanks in advance.


#2
pub fn new(...) -> Result<ReleaseContext<'a, R>, RepoErrors> {
    match RepoImpl::<Repository>::open(path) {
        Ok(or) => Ok(ReleaseContext {
            ...
            repo: or,
        }),
        ...
    }
}

`R` is an input type parameter, it is controlled by the caller not by this function. The function promises to construct a `ReleaseContext<R>` regardless of what `R` actually is, it can only assume that `R` implements `Repo`. It can't just put a concrete type there.

You can return "something implementing `Repo`" by using [trait objects](http://doc.rust-lang.org/book/trait-objects.html) (e.g. put `repo: Box<Repo<'a>>` in the struct) or wait for some kind of "abstract return type" feature (see [the latest "impl Trait" RFC](https://github.com/rust-lang/rfcs/pull/1522)).

#3

After some reading, I suspected Box was going to be the answer. I was hoping there was another way.

Thanks.