Returning an error code from a function?

So i’m writing a pretty simple function. It will look through a list of objects (Appendage class) and it will choose one of them and return it, along with an integer code that indicates if the selection was sucessful, or a reason why it failed.

But as far as i’m aware, a function can only return a single value. So i’m not quite sure how to do that,

The next solution that comes to mind, would be creating another new, simple class, that just holds an Appendage, and an integer. I guess i can do that, but it seems like overkill just for the return value of one rarely used function.

Surely returning error codes along with values/references is a common thing to do. Is there a better way to do it?

Perhaps you could use out?

if you’re returning an integer value there is always the “errors are negative numbers” approach, always feels a little “hacky” though, be interesting to see if there is a better way of doing it :wink:

Crayz already suggested an out parameter:

What I’m wondering though is you refer to 1 success scenario, and multiple fail scenarios. Which sounds like the standard exception scenario.

Exception scenario, as the name implies, is that it succeeds with exception. Either you succeeded or an exception (error) occurred.

So no need for an out param, or multiple out states. You return the answer, and if it failed you throw an exception.

https://msdn.microsoft.com/en-us/library/ms173163(v=vs.80).aspx

I’d go about it as:

Appendage FindAppendage(string searchParam)
{
    if(couldntBeFaffed) throw new FailedAppendageSearchException(FailedAppendageSearchReason.CouldnotBeFaffedToBotherSearching);
 
    //... do search
}

public class FailedAppendageSearchException : System.Exception
{

    public readonly FailedAppendageSearchReason Reason;

    public FailedAppendageSearchException(FailedAppendageSearchReason reason)
    {
        this.Reason = reason;
    }
 
}

public enum FailedAppendageSearchReason
{
    OutOfRange,
    HadRabies,
    CouldnotBeFaffedToBotherSearching,
    LeaveMeAlone
}

usage:

void Example()
{
    Appendage result;
    try
    {
        result = FindAppendage("blargh");
    }
    catch(FailedAppendageSearchException ex)
    {
        Debug.Log(ex.Reason);
    }
}
2 Likes

Returning error codes is a very c-ish thing, and not very common in C# at all - or in general in OO land. It has some clear drawbacks - primarily that you can’t be sure that the error code will be used when the method is used. So if you have an error code along the lines of “don’t use the returned object, as it will break everything”, that doesn’t mean that somebody else (ususally yourself two months down the line) won’t mess up and use it anyway.

Throwing errors is usually the way to indicate that something went wrong. You can throw several errors, and handle them differently with different catch clauses. If there are instances where you will receive an error message in addition to an appendage that you’re going to use, that’s not going to help (throwing the error means that no value will be returned), but that’s seldom the case.

Note you also have Debug.LogError. It’s a Unity specific function that’s the equivalent of throwing an error explicitly. I tend to use it for a lot of light weight stuff.

Not sure if you can catch the error on this through. Anyone tried it?

I have tried, and couldn’t do it:

try {
    Debug.LogError("SomeError");
}
catch {
    Debug.Log("This will not get printed");
}

So if you actually want to catch something, throw a default exception:

try {
    throw new Exception("Some Error");
}
catch {
    Debug.Log("This will get printed");
}

EDIT:
Note that these will both show in the editor, so the difference is in control flow, not presentation.

A thrown error backtracks up the current flow until it finds a try-catch block that contains the current flow, and accepts the type of the error. Since Unity can keep running even if exceptions are thrown, I’m assuming that the internal part that does calls to Update (and all other functions) is wrapped in a try-catch. This means that the game will keep running, but throwing an error WILL end all execution of the current Update (or FixedUpdate or whatever) on whatever MonoBehaviour that called it, whereas LogError will allow the flow to continue.

2 Likes

this was an amazing post, funniest thing i’ve seen all day <3

As to that specific thing you mention, i don’t think that is the case. Some context;
I’m using this as part of some AI code for a multi-limbed creature (right now a dog, but i intend to make it useable for an octopus too). to determine which of its appendages it will use for a given task (like bieng ordered to pick up a teddybear.)

It’ll check the appendages for such things like; Whether that appendage is even able to grasp things, if that appendage isn’t currently holding something, if it isn’t currently being used for locomotion, if it’s within range of the target object, if it’s large enough to grab the volume of the target object.
And leading on from those, possible errors/exceptions might be that all limbs are busy, or the target is too big for any of them to hold, or that it’s too far out of reach (which would then trigger an order to walk closer and try again). But now that i think on the subject, there will probably need to be more than one success condition. Like, the object is too big for any single limb, so here’s two tentacles returned, use them both in sync to grasp it.

Currently reading through these replies. The Out parameter seems like a good solution so far. Throwing exceptions seems like something i should learn to do, even if not for this particular application

This sounds right for the use case you’ve described. Errors shouldn’t be used to control the flow of code, only when things go wrong. An arm not being able to lift something is not an exception in the usual sense. An exception is something like a limb not existing.

Look to Physics.Raycast for an example of this in action. The function returns a bool to indicate success or failure, and the data is provided via out.

The other way is to provide all of the data in a custom class that you return.

1 Like