Difference between `struct Toto;` and `struct Toto {}`

Hello,
I am trying to use dynamic traits and I noticed that struct Toto; and struct Toto {} are not the same kind of unit struct for the compiler.

I noticed this because the three following code examples compile:

pub trait Interface {
    fn func(&self);
}
struct Toto; 
impl Interface for Toto {
    fn func(&self) {
        println!("Toto");
    }
}
pub fn toto() -> &'static dyn Interface {
        &Toto {}
}
pub trait Interface {
    fn func(&self);
}
struct Toto; 
impl Interface for Toto {
    fn func(&self) {
        println!("Toto");
    }
}
pub fn toto() -> &'static dyn Interface {
        &Toto
}
pub trait Interface {
    fn func(&self);
}
struct Toto {}
impl Interface for Toto {
    fn func(&self) {
        println!("Toto");
    }
}
pub fn toto() -> &'static dyn Interface {
        &Toto {}
}

but not this one:

pub trait Interface {
    fn func(&self);
}
struct Toto {}
impl Interface for Toto {
    fn func(&self) {
        println!("Toto");
    }
}
pub fn toto() -> &'static dyn Interface {
        &Toto 
}

which produce the following error:

error[E0423]: expected value, found struct `Toto`
  --> src\lib.rs:11:10
   |
4  | struct Toto {}
   | -------------- `Toto` defined here
...
11 |         &Toto
   |          ^^^^ help: use struct literal syntax instead: `Toto {}`

For more information about this error, try `rustc --explain E0423`.
error: could not compile `dyn-tray` (lib) due to previous error

Why can I use &Toto or &Toto {} when Toto is define as struct Toto; but only &Toto {} when Toto is defined as struct Toto {}?

2 Likes

Because the StructName { ... } syntax is considered as the fundamental one, and it is always available no matter what:

struct Foo(i32, i32);

fn main() {
    let bar = Foo {
        0: 10,
        1: 30,
    };
}

When you define a struct with struct Toto; you actually do two things:

  • Define the struct.
  • Declare a constant called Toto that is equal to Toto {}.

Similarly, defining a struct with struct Toto(i32, i32); also does two things:

  • Declare the struct.
  • Define a function called Toto that takes two arguments and constructs the struct.

You can see that it defines a function because writing Toto will give you a function pointer.

28 Likes

You also enable Toto as a pattern.

4 Likes

I could be misremembering this, but I seem to recall this not always being true. Specifically, you couldn't use Toto {} in the case of struct Toto;. One of the arguments for changing this was specifically to allow macros to write literals and patterns for "structs with no fields" with the same syntax as "struct with named fields". Basically just treating them as a degenerate case.

That said, I was not aware this also worked for tuple structs. Thank you for using that example!

2 Likes

Strictly speaking, declaring a constant implies that:

const FOO: u32 = 10;

fn main() {
    for i in [0, 10, 17, 20, 30] {
        match i {
            17 => println!("seventeen"),
            FOO => println!("foo"),
            num => println!("number is: {num}"),
        }
    }
}
number is: 0
foo
seventeen
number is: 20
number is: 30

Interesting, but this doesn't quite work for structs:

struct Toto {}

const Toto: Toto = Toto {};

fn main() {
    match (Toto {}) {
        Toto => {}
    }
}
error: to use a constant of type `Toto` in a pattern, `Toto` must be annotated with `#[derive(PartialEq, Eq)]`
 --> src/main.rs:7:9
  |
7 |         Toto => {}
  |         ^^^^
  |

4 Likes

Okay, I guess I was wrong. And tuple structs definitely requires extra support beyond just declaring a function.

2 Likes

Thanks a lot for the answer.

So is there a difference between struct Toto; and struct Toto {}, besides how you can instantiate them?

I thought maybe the former always refers to one singular object, but I made a playground where I implemented Drop for it and it got called twice when I instantiated it twice, so I'm clearly wrong about that.

No, the differences are only syntactical.

2 Likes

Technically, the distinction is that struct Toto; essentially means

struct Toto {}
const Toto: Toto = Toto {};

So it's the same for the type, but different in that it also puts something under that name into the value namespace as a constant.

See https://rust-lang.github.io/rfcs/1506-adt-kinds.html#unit-structs for more.

1 Like