Skip to content

Language feature: Named variables in function return declarations #1999

@IvankoB

Description

@IvankoB

With visibility of function parameters, possibly with default return values, to achive functionality of Delphi's function "Result" auto variable or as shown in the below "Go" code:

func OpenPrinter(name string) (success bool, printer HANDLE) {
openPrinter := winspool.NewProc("OpenPrinterA")
Cname := unsafe.Pointer(C.CString(strToAnsi(name)))
defer C.free(Cname)
Cret, _, _ := openPrinter.Call(uintptr(Cname), uintptr(unsafe.Pointer(&printer)), uintptr(0 /Cnil/))
success = (int(Cret) != 0)
return
}

Note the usage of return varibales "success" & "printer" within the function body. Very handy & clear.
The current "Rust" code for this function is as follows :

fn open_printer(name: String) -> (bool, HANDLE) {
let h: HANDLE = ptr::null_mut();
unsafe {
(OpenPrinterW(str2wcstr(&*name).as_ptr(), &h, std::ptr::null_mut()) == TRUE, h)
}
}

Activity

mark-i-m

mark-i-m commented on May 11, 2017

@mark-i-m
Member

I don't have strong feelings, but it feels "unrustic" to return a value by assigning to named return variable. Also, this might just be me, but I find the rust code you posted much more readable than the Go code you posted...

SimonSapin

SimonSapin commented on May 11, 2017

@SimonSapin
Contributor

This sounds like "structural records". Rust used to have them, and removed them in 0.6:

Adding them back was proposed in https://internals.rust-lang.org/t/pre-rfc-unnamed-struct-types/3872 . As far as I can tell this proposal was not explicitly rejected, the conversation just died out. It may be worth writing a formal RFC pull request.

burdges

burdges commented on May 11, 2017

@burdges

You could rephrase this as asking to place a pattern in return position and assign to it. And you might as well ask for patterns in argument position while you're at it. I donno if either really buys much, but it might look like

fn foo((a,mut b) : (usize,usize)) -> Range { mut start, mut end } : Range<usize> { ... }

or maybe you could omit the muts in return position so long as you only assigned to each once.

IvankoB

IvankoB commented on May 11, 2017

@IvankoB
Author

One more advantage of this approach is that no need explicitly to maintain stack variables to return within function - those (if declared as proposed) are automatically created & managed on function calling & returning. Who programmed in Delphi definetely loved its "result" auto variable.

IvankoB

IvankoB commented on May 11, 2017

@IvankoB
Author

You could rephrase this as asking to place a pattern in return position and assign to it

Yes. In order that not to create this pattern within function.

IvankoB

IvankoB commented on May 11, 2017

@IvankoB
Author

code you posted much more readable than the Go code

Yes, it's short but note the "&h -> h" l-value usage in the return calculation.

burdges

burdges commented on May 11, 2017

@burdges

I think Rust already requires the caller allocate the space for returned structs @IvankoB but maybe one could optimize it for tuples by passing pointers for each component instead of one pointer for the whole tuple.

I think non-lexical lifetimes might enable this without adding complexity to the fn declaration, so maybe

fn foo(..) -> Range<Foo> {
    let Range { start, mut end } = r;
    end = 0;
    for ... { end += baz; }
    start = bar;
    r
}

as opposed to

fn foo(..) -> Range<Foo> {
    let mut r = 0..0;
    {
        let Range { start, mut end } = r;
        end = 0;
        for ... { end += baz; }
        start = bar;
    }
    r
}
IvankoB

IvankoB commented on May 11, 2017

@IvankoB
Author

let Range { start, mut end } = r;

It's an explicit creation of stack variables which will be returned when function finishes. The ability of returning stack variables as ref counted function results is very handy fetarure of Rust. With the proposal in simple cases it can be simplified even more.

wesleywiser

wesleywiser commented on May 11, 2017

@wesleywiser
Member

Wouldn't the "rustic" way of writing this actually use Result or at least Option? For example, this is what the libstd does:

    pub fn open(path: &Path, opts: &OpenOptions) -> io::Result<File> {
        let path = to_u16s(path)?;
        let handle = unsafe {
            c::CreateFileW(path.as_ptr(),
                           opts.get_access_mode()?,
                           opts.share_mode,
                           opts.security_attributes as *mut _,
                           opts.get_creation_mode()?,
                           opts.get_flags_and_attributes(),
                           ptr::null_mut())
        };
        if handle == c::INVALID_HANDLE_VALUE {
            Err(Error::last_os_error())
        } else {
            Ok(File { handle: Handle::new(handle) })
        }
    }

https://github.com/rust-lang/rust/blob/2cc3358e4f1c4a79685745a461a1be1ce784b88a/src/libstd/sys/windows/fs.rs#L257

IvankoB

IvankoB commented on May 11, 2017

@IvankoB
Author

Our collegue "burdges" has got the idea.

mark-i-m

mark-i-m commented on May 11, 2017

@mark-i-m
Member
fn foo(..) -> Range<Foo> {
    let Range { start, mut end } = r;
    end = 0;
    for ... { end += baz; }
    start = bar;
    r
}

Sorry, I'm still not convinced. For me this piece of code was genuinely confusing for a minute until I realized r is the implicit return value. I can't think of anywhere else in rust where variables can just appear out of nowhere like that...

added
T-langRelevant to the language team, which will review and decide on the RFC.
on May 12, 2017
burdges

burdges commented on May 12, 2017

@burdges

Yeah I realized it'd need an explicit name like fn @mark-i-m

I've no idea if this is a good idea, but I wanted to point out that destructuring can happen in the body without fancy new syntax in the fn declaration.

IvankoB

IvankoB commented on May 12, 2017

@IvankoB
Author

This feature also involves empty return ("Go") or no return at all ("Delphi").

mark-i-m

mark-i-m commented on May 12, 2017

@mark-i-m
Member

@IvankoB what do you mean by "empty return" and "no return at all"? Are they analogous to return () or ! ?

31 remaining items

Loading
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    T-langRelevant to the language team, which will review and decide on the RFC.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @SimonSapin@burdges@wesleywiser@Centril@sfackler

        Issue actions

          Language feature: Named variables in function return declarations · Issue #1999 · rust-lang/rfcs