ruststructvisibility

Puzzling visibility issues with Rust tuple-like structs


I am encountering a puzzling error when creating a tuple-like struct somewhere in my Rust project.

The error

I boiled my issue down to the following snippet:

mod parent {
    pub struct X(u32);

    mod child {
        use crate::X;

        fn f() {
            let x = X(42);
        }
    }
}

use parent::X;

In my head, this snippet should compile just fine. X is public, and its field is visibile within parent, so all its children (including child::f) should be able to access it. However, when I go and compile that snippet, I get

error[E0423]: expected function, tuple struct or tuple variant, found struct `X`
 --> src/main.rs:8:21
  |
8 |             let x = X(42);
  |                     ^

For more information about this error, try `rustc --explain E0423`.
error: could not compile `playground` (bin "playground" test) due to 1 previous error

Weirdly enough, the error message mentions the compiler expecting a "tuple variant", but finding X instead. But, X is a tuple variant!

Path dependence

The problem disappears when I use super::X instead of crate::X.

mod parent {
    pub struct X(u32);

    mod child {
        use super::X;

        fn f() {
            let x = X(42);
        }
    }
}

use parent::X;

This is even more puzzling to me. Why would the correctness of my snippet depend on the specific path I use to use X? In my mind, X is X, wherever I import it from.

With non-tuple structs

The problem also disappears when I give X named fields:

mod parent {
    pub struct X {
        x: u32,
    }

    mod child {
        use crate::X;

        fn f() {
            let x = X { x: 42 };
        }
    }
}

use parent::X;

Note that I am using use crate::X in this snippet, just as in the first one! Why would this error appear only with tuple-like structs? I am very confused. I am usually not one to ever doubt the correctness of the Rust compiler, but I have to admit that today my faith is being tested.

Any insight on what I might be missing?


Solution

  • This is the same problem as in #74139.

    Imports filter away names that are private at the import point, that's a pretty fundamental rule. The Foo's constructor is private to mod a, so it's filtered away.

    So this is not a bug, but the error should probably have a note mentioning the private constructor. Such note is emitted in some other cases but not in this one.

    ā€“ petrochenkov commented Jul 8, 2020

    Only the private function X (each tuple struct implicitly defines a constructor function in addition to the struct itself) is filtered out so using regular struct syntax does work (for both struct types):

    mod parent {
        pub struct X(u32);
    
        mod child {
            use crate::X;
    
            fn f() {
                let x = X{ 0: 42 };
            }
        }
    }
    
    use parent::X;
    

    Playground