Skip to content

Instantly share code, notes, and snippets.

@bmizerany
Last active December 1, 2018 22:47
Show Gist options
  • Select an option

  • Save bmizerany/fcd0348bda96edce05a4fc7426e47751 to your computer and use it in GitHub Desktop.

Select an option

Save bmizerany/fcd0348bda96edce05a4fc7426e47751 to your computer and use it in GitHub Desktop.
package question
func whatsTheEquivalentOfThisWithHandleCheckQuestionMark(w io.Writer) (rN int, err error) {
w = LimitedWriter{w, 23}
n, err := io.WriteString(w, "some data 1") // 11 bytes written
rN += n
if err != nil {
return err
}
n, err := io.WriteString(w, "some data 2") // 22 bytes written
rN += n
if err != nil {
return err
}
n, err := io.WriteString(w, "some data 3") // 23 bytes written: BOOM!!!
rN += n
return rN, err
}
func canItBeThisQuestionMark(w io.Writer) (n int, err error) {
handle err { return n, err }
w = LimitedWriter{w, 23}
n += check io.WriteString(w, "some data 1") // 11 bytes written
n += check io.WriteString(w, "some data 2") // 22 bytes written
n += check io.WriteString(w, "some data 3") // 23 bytes written: BOOM!!!
return nil
}
@kardianos

Copy link
Copy Markdown

ItLooksLikeYouAreTryingToAskAboutTheOrderOfAssignmentVsErrorChecking.

@monokrome

monokrome commented Aug 29, 2018

Copy link
Copy Markdown

Seems that this comment before me is sarcastic and maybe not helpful...

If a partial write is a type of error then can you handle it within the handle block? When the handle block doesn't return, does the function continue to execute?

@kardianos

Copy link
Copy Markdown

It looks like you are trying to ask about the order of assignment vs error checking.

In the future if you don't present introductory text, a comment above the two functions would be much easier to read.

I wasn't trying to be sarcastic, but I was trying to point out that attempting to communicate in this way is somewhat annoying. I could have done better. I'm sorry.

@kalexmills

Copy link
Copy Markdown

Looks like @bmizerany is asking whether the handle block returns just an error, or if it has to return every return value defined by its inner function.

Take a look at the line marked "handler A" in this example from the detailed design draft.

func process(user string, files chan string) (n int, err error) {
    handle err { return 0, fmt.Errorf("process: %v", err)  }      // handler A
    for i := 0; i < 3; i++ {
        handle err { err = fmt.Errorf("attempt %d: %v", i, err) } // handler B
        handle err { err = moreWrapping(err) }                    // handler C

        check do(something())  // check 1: handler chain C, B, A
    }
    check do(somethingElse())  // check 2: handler chain A
}

Based on this, my understanding is that any return statement defined inside a handler would need to include the entire return signature. To me, this is less surprising and results in code which is easier to read.

@bmizerany

bmizerany commented Sep 6, 2018

Copy link
Copy Markdown
Author

@kalexmills I'm wondering if the addition is performed before the error passed to handle. If it isn't the case that the addition is performed, then will the code I wrote compile? It seems to me this could be something the compiler will aid in catching.

@elimisteve

elimisteve commented Sep 8, 2018

Copy link
Copy Markdown

My hope is that canItBeThisQuestionMark would not compile, and that instead we'd have to write

func canItBeThisQuestionMark(w io.Writer) (n int, err error) {
	handle err { return n, err }
	w = LimitedWriter{w, 23}
	nb := check io.WriteString(w, "some data 1") // 11 bytes written
	n += nb
	nb = check io.WriteString(w, "some data 2") // 22 bytes written
	n += nb
	nb = check io.WriteString(w, "some data 3") // 23 bytes written: BOOM!!!
	n += nb
	return n, nil
}

even though it is not as clean.

@nigeltao

Copy link
Copy Markdown

@elimisteve your version returns n = 22, not 23, right?

@baryluk

baryluk commented Dec 1, 2018

Copy link
Copy Markdown

I do not see a problem.

With handle err { return n, err }, it will return 22, err

With handle err { return n, err }, it will return 0, err

Programmer is in control. Sometimes it might be exactly what you want. I.e. what if you are consuming data from a socket or pipe, or parsing, and you find an error only at position x. Similarly if the WriteString is to a socket. You can't unread / unwrite this data.

If you are asking about whatever n will be updated, in a situation where WriteString returns (1, SomeError), then this 1 will be ignored, and n += 1, will not be performed.

In a situation like this you would write:

n3, err = io.WriteString(w, "some data 3")
n += n3
check err

maybe?

This is because n += check X()

is rewritten to:

``
temp, err := X()
if err != nil { call handler with err; return; }
n += temp


This is the same as using normal `:=` operator:

``
temp, err := X()
if err != nil { call handler with err; return; }
n := temp

Notice that in Go you can't express assignment to err, and increment of n in a single statement anyway.

It does mean however than

n = check X()

and

n, err = X()
if err != nil { return err; }

do have different semantic (and the first one would use extra register / stack space). This probably only applies to return values tho. In other cases, compiler should be able to do the same as before.

I did not see the semantic explained precisely in current drafts tho.

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