Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New lint: Attempted re-export using pub type hides struct's implicit constructor #338

Open
Tracked by #5
obi1kenobi opened this issue Jan 31, 2023 · 3 comments
Open
Tracked by #5
Labels
A-lint Area: new or existing lint C-enhancement Category: raise the bar on expectations

Comments

@obi1kenobi
Copy link
Owner

Re-exporting types allows maintainers to move or rename types without breaking downstream users. Usually pub use is used for this purpose, but some uses of pub type work too and are used by some maintainers.

Unfortunately, pub type does not allow unit and tuple structs to be constructed using their usual unit/tuple syntax (see examples below). Even though the curly-braces syntax is also allowed for unit/tuple structs and would still work with pub type, it's practically guaranteed to not be used since it isn't idiomatic in these cases. That makes this a major breaking change.

This requires new schema that would allow the lint to track whether each importable path points to:

  • the type definition itself,
  • a pub use, which still allows the unit and tuple constructor syntax,
  • a pub type type alias, which is the only case that could have this major breaking change.

Only externally-constructible structs are affected. Structs with private fields or with the #[non_exhaustive] attribute are not affected. (Pattern-matching a tuple that constructible from the given crate is only allowed with the curly-braces syntax. Foo(x, ..) is not allowed outside of Foo's defining crate if it is not externally-constructible, so tuple patterns aren't the cause of additional breakage. Tuple structs with both public and private fields are therefore not broken by a pub type re-export.)

Unit struct example: playground link

pub(crate) mod inner {
    pub struct Bar;
}

pub type Foo = inner::Bar;

fn f() -> inner::Bar {
    Foo  // Error!
}

produces:

error[E0423]: expected value, found type alias `Foo`
 --> src/lib.rs:8:5
  |
8 |     Foo
  |     ^^^
  |
  = note: can't use a type alias as a constructor

Tuple struct example: playground link

pub(crate) mod inner {
    pub struct Bar(pub i64);
}

pub type Foo = inner::Bar;

fn f() -> inner::Bar {
    Foo(1)
}

produces:

error[E0423]: expected function, tuple struct or tuple variant, found type alias `Foo`
 --> src/lib.rs:8:5
  |
8 |     Foo(1)
  |     ^^^
  |
  = note: can't use a type alias as a constructor
@obi1kenobi
Copy link
Owner Author

It's possible that with sufficient effort, users might be able to make this kind of change without triggering a semver-major change. Based on an idea shared on r/rust, the crate author making this change may be able to add a const (for a unit struct) or a fn (for a tuple struct) with the same name as the pub type and have it act as the constructor that was lost in the pub type.

For example, one may be able to replace pub struct Foo(pub u8) with something like:

pub(crate) inner {
    pub struct Bar<T>(pub T);
}

pub type Foo = inner::Bar<u8>;

#[allow(non_snake_case)]
pub fn Foo(v: u8) -> Foo {
    inner::Bar(v)
}

Before implementing this lint, we should:

  • Determine whether the above workaround actually works correctly in all cases, both for unit and for tuple structs.
  • Add such workarounds as test cases.
  • Ensure the lint does not flag the workarounds as semver-major, since that'd be a false-positive (if the workaround is indeed equivalent).

@obsgolem
Copy link

obsgolem commented Feb 1, 2023

Can't you pattern match on tuple structs using their constructor though? You won't be able to do that with a function.

@obi1kenobi
Copy link
Owner Author

Good point! So perhaps this is still breaking for constructible (exhaustive + no private fields) tuple and unit structs?

Is there a reason non-constructible (meaning, with a literal) tuple and unit structs might break if re-exported with a pub type?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-lint Area: new or existing lint C-enhancement Category: raise the bar on expectations
Projects
None yet
Development

No branches or pull requests

2 participants