Skip to content

Instantly share code, notes, and snippets.

@bmizerany
Last active December 1, 2018 22:47
Show Gist options
  • Save bmizerany/fcd0348bda96edce05a4fc7426e47751 to your computer and use it in GitHub Desktop.
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

ItLooksLikeYouAreTryingToAskAboutTheOrderOfAssignmentVsErrorChecking.

@monokrome
Copy link

monokrome commented Aug 29, 2018

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

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

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
Copy link
Author

bmizerany commented Sep 6, 2018

@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
Copy link

elimisteve commented Sep 8, 2018

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

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

@baryluk
Copy link

baryluk commented Dec 1, 2018

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