Allow returning T where T: Default

The best example would be implementation of C’s void type in Rust:

#[repr(transparent)]
struct void(u8);

impl Default for void {
fn default() -> Self {
Self(0)
}
}

fn main() {
assert_eq!(void::default(), foo());
}

fn foo() -> void {
void
}

or with u8:

fn foo() -> u8 { u8 }

fn main() {
assert_eq!(0u8, foo());
}

In my mind this works like unit-structs :face_with_monocle:

What problem does this solve? Can't you already do void::default() and u8::default?

Unit structs just declare a const with the same value as the struct. You can already do this if your struct is not a tuple struct (since those also declare a function with the same name as the struct, and that conflicts with the const)

#[derive(Debug, PartialEq, Eq)]
struct void {
    i: u8,
}

impl Default for void {
    fn default() -> void {
        void
    }
}

const void: void = void { i: 0 };

fn main() {
    assert_eq!(void::default(), foo());
}

fn foo() -> void {
    void
}

6 Likes

C's void return type in Rust is just (). libc::c_void even says:

this is not the same as C’s void return type, which is Rust’s () type.

A Rust function shouldn't be trying to return C's void.

4 Likes

Thank you for explanation

@mjbshaw, I do not need void and core::ffi::c_void doesn’t seem really good because it is an enum. Also unit is not void cuz these types even have different sizes (unit is ZST and void is one byte). Void implementation was only an example.

(last post on void; sorry for kinda derailing this)

C's void doesn't have a size. You can't perform sizeof(void). If you're talking about Rust then it's up to the underlying implementation of void (IMO libc::c_void would ideally just be an extern type and would thus not have a size, just like C's void).

2 Likes

This is not the translation of C's void foo();.

void means two different things in C, and the definition you used here is the one that's a reasonable translation of void as in void*, but not as in void functions.

void actually has a size. Try to compile

#include <stdio.h>

void main() {
printf("%d", sizeof(void));
}

This one prints "1".

Also @scottmcm, please, stop talking about C and void - this is actually not the theme of this conversation.

That's a non-standard GCC extension: Pointer Arith - Using the GNU Compiler Collection (GCC)

2 Likes

This printed 0 for me (Windows with MSVC)

1 Like

To be fully explicit

The equivalent of C void foo(void); is Rust extern { fn foo(); }. Writing Rust -> () (implicit with no return type) is guaranteed the same as returning void in C.

To translate C void*, use Rust *mut std::ffi::c_void.

Again restating for full explicitness, these three uses of void in C mean different things.

  • C void as a return type => Rust -> ()
  • C void as a parameter type => marks that a function is not the old, deprecated, removed in C23, pre-K&R declaration without specifying arguments[1], and is in fact a nullary function.
  • C void as a pointee type => marks that pointer as pointing to arbitrary data that cannot be described without casting the pointer.
  • A C stack local binding of type void is 100% forbidden and I don't know of any compiler extension which allows it.

  1. Since C89 and up until C23, void foo(); was a deprecated way of declaring the function foo as returning nothing but not declaring what arguments it takes, so you could call it with any combination of arguments, e.g. foo(1, 2, 3), and were just assumed to call it with the correct arguments for the linked implementation. Early pre-K&R was wild; even struct field names had to be globally unique because absolutely no type checking was typically done when C was purely a slightly more portable assembler. ↩︎

1 Like

This could be done. But why would we? What code does this make better? Where did you want to use it? Presumably not for -> (), where the current thing is fine.

I think it's a feature, not a problem, that

fn foo() -> VecDeque<String> {}

doesn't compile. Having that invisibly allocate memory for a VecDeque -- which is Default! -- seems very strange to me.

Even for Default + Copy things,

fn bar() -> u8 {}

also doesn't seem useful. Having it be { 0 } instead seems way better, for negligible extra effort from the programmer.

10 Likes

@notriddle, can you please close this thread? It is rolling down into the pit of uselessness because users don't really read previous replies.

Sure, I’ll go ahead and do that.

If anybody else wants this, or any other, locked thread to be re-opened, use the Flag button with the “other” reason. We usually will open it if there’s something new to add to it.