On 24 August 2015 at 19:22, Nate Finch <nate.fi...@canonical.com> wrote: > That's fair. I wasn't sure if recovering the panic in errors would be a > good idea, since it could hide programmer error (like what I'd done). But > I'm fine with that solution.
To be fair, I'm not entirely sure that panic recovery is a good idea either, but fmt sets the precedent to follow here. > On Mon, Aug 24, 2015 at 3:22 AM David Cheney <david.che...@canonical.com> > wrote: >> >> Yup, I agree with Rog. This is fixing the problem in the wrong place. >> >> On Mon, Aug 24, 2015 at 5:14 PM, roger peppe <roger.pe...@canonical.com> >> wrote: >> > I'm afraid I'm not convinced. Declaring the Error method on the >> > pointer receiver is idiomatic (just grep for ' Error\(' in the Go >> > source) >> > and is a useful indicator that the error value is always intended >> > to be a pointer. >> > >> > There's a much simpler fix for this: let the errors package >> > recover from this itself. We can just make Err.Error call fmt.Sprint >> > to get the error message (a one line change) >> > >> > Then a wrapped nil error will print <nil> just like normal nil >> > errors. >> > >> > >> > On 20 August 2015 at 03:45, Nate Finch <nate.fi...@canonical.com> wrote: >> >> tl;dr: Don't. Use a value receiver. 99% of the time you can just >> >> delete >> >> the * on the receiver and it'll still work. If you really must use a >> >> pointer, please handle the case where you're called with a nil >> >> receiver. >> >> >> >> I spent most of today trying to understand why my new hook command was >> >> producing this output: >> >> >> >> error: %!v(PANIC=runtime error: invalid memory address or nil pointer >> >> dereference) >> >> >> >> It took me a while to figure out that this is what fmt.Printf("error: >> >> %v\n", >> >> err) outputs when err's Error() method panics. If you're using %s or >> >> %v to >> >> print a value (or use Println which implicitly uses %v), then fmt will >> >> look >> >> for a String() method or Error() method on the value to call, and use >> >> the >> >> output of that for the value's string output. If that method panics, >> >> fmt >> >> prints the panic in the way you see above (everything after the >> >> PANIC=). >> >> >> >> Of course, the problem here is that there's no type being written, and >> >> since >> >> it was an error interface, it could almost anything. Using %#v skips >> >> calling the Error/String methods and prints out the values in a go >> >> format, >> >> which told me this was a juju/errors.Err value, wrapping an API params >> >> Error >> >> value which was a nil pointer. When we call Error() on an errors.Err, >> >> we >> >> call Error() on the cause explicitly, which was panicking. >> >> >> >> Here's a minimal reproduction http://play.golang.org/p/ncNVrza-hn >> >> (you'll >> >> have to copy it to a local file and go run it, since the playground >> >> won't >> >> run code external to the stdlib). >> >> >> >> So what's sort of interesting is that printing the error before it gets >> >> Traced works fine, but after the trace it is not fine. The >> >> errors.Err's >> >> Error() function looks like it's explicitly calling the Error() method >> >> on >> >> the wrapped Cause error, which is probably the problem. fmt.Printf >> >> must use >> >> some reflection magic to avoid doing that. >> >> >> >> Now, the root cause of this particular bug is actually my own mistake. >> >> Line >> >> 21 should check if orig is nil and then assign nil explicitly to err if >> >> it >> >> is. Then errors.Trace would be able to tell that the error is nil and >> >> would >> >> just return nil itself, instead of thinking it's a valid error and >> >> wrapping >> >> it. >> >> >> >> However, you can sidestep this entirely by doing one of two things: >> >> either >> >> just make the Error() (or String()) method use a value receiver.. in >> >> which >> >> case this code would produce this output: >> >> >> >> %!v(PANIC=value method main.MyError.Error called using nil *MyError >> >> pointer) >> >> >> >> (you can try it with the repro code I linked to) >> >> >> >> This printout is a lot more helpful and useful and obvious than the >> >> other >> >> "nil pointer" printout. >> >> >> >> OR >> >> >> >> Just handle a nil receiver: >> >> >> >> func (e *MyError) Error() string { >> >> if e == nil { >> >> return "<nil MyError>" >> >> } >> >> return e.Message >> >> } >> >> >> >> (note that it is dereferencing the pointer to e to access the Message >> >> field >> >> which causes the panic. Calling a method on a nil pointer is totally >> >> fine >> >> and will not panic if the code inside does not try to derefence the >> >> pointer >> >> to get to a field). >> >> >> >> Grepping through our code, I see a lot of pointer receivers on Error >> >> and >> >> String methods (45 and 77 respectively). I think we should at least >> >> change >> >> all of these to be value methods (unless that's not possible. That's >> >> a >> >> trivial change, and gives a much more useful printout when the pointer >> >> is >> >> nil. >> >> >> >> -Nate >> >> >> >> -- >> >> Juju-dev mailing list >> >> Juju-dev@lists.ubuntu.com >> >> Modify settings or unsubscribe at: >> >> https://lists.ubuntu.com/mailman/listinfo/juju-dev >> >> >> > >> > -- >> > Juju-dev mailing list >> > Juju-dev@lists.ubuntu.com >> > Modify settings or unsubscribe at: >> > https://lists.ubuntu.com/mailman/listinfo/juju-dev -- Juju-dev mailing list Juju-dev@lists.ubuntu.com Modify settings or unsubscribe at: https://lists.ubuntu.com/mailman/listinfo/juju-dev