#Nullable Reference Types

1 messages · Page 1 of 1 (latest)

glacial minnow
#

Do you ever use NRTs in your Unity projects? I found out it's a great feature to make code safer at compile-time. I never saw it used in the context of Unity though. Do you use it at all? Do you enable it on the project level or only for some specific/new files? Is it better to stick to one side of it or always prefer to use it when possible?

I tested it out in a separate C# project and figured that I can safely combine both approaches and I guess I could do that for my new code, but I fear that it will make it more obscure and hard to read since I will have to always keep in mind if I am working in a nullable or non-nullable context.

tender juniper
#

unity has 2 flavors of null, so using that doesn't completely make nullish stuff safe

glacial minnow
#

What do you mean by 2 flavors of null?

tender juniper
#

when unity Objects get destroyed, the backing c++ structure is destroyed and has null references, but the managed c# reference can't just turn into null wherever it is currently referred to
so it's not actually null, but unity makes it act like null

glacial minnow
#

oh, the MissingReference?

#

so it makes the whole idea of using nullables pointless?

tender juniper
#

i don't remember the exact names, but yes, that

tender juniper
prime vapor
#

I do use NRT in mine, and it's great especially because my project is very heavily code driven.
On most Unity projects, there are some headaches associated with NRT that the tradeoff might be harder to judge. Specifically serialized fields/properties will trip NRT.

glacial minnow
#

okay so unless I am destroying Unity objects (or use systems that do that implicitly) I should be good with nullables?

tender juniper
#

unity's fake null is also used for unset serialized references iirc?

glacial minnow
tender juniper
#

but also - unity destroys unity objects, as part of their lifecycle

glacial minnow
glacial minnow
prime vapor
#

Which is a fake warning if you use inspector to drag an enemy to it so it's not actually null.

glacial minnow
#

If devoting to using nullables then the correct form would be GameObject?, no?

#

because it can be null

prime vapor
#

If it's meant to be nullable, then sure. If it's not meant to be nullable (eg you have ways to ensure the enemy is always assigned in inspector) then you are just polluting your code with a bunch of unnecessary null checks.

glacial minnow
#

Yeah that's true though

#

I guess you use it for contexts other than serialization?

prime vapor
#

Eh, you technically can enable/disable NRT on a per file basis, but that's way too much effort.

glacial minnow
prime vapor
#

If you decide NRT is worth it, then you should just use it everywhere, and in serialization cases you just do something like GameObject _enemy = null!; or pragma to suppress the warning.

glacial minnow
#

hm yeah...

#

do Asserts act as checks btw?

#

if I Assert.IsNotNull(_enemy); and then do whatever

prime vapor
#

That's from testing framework, no?

#

You wouldn't want testing framework to be in your runtime code.

glacial minnow
#

no, it's from UnityEngine.Assertions

#

it's also conditional under "UNITY_ASSERTIONS" so it is not included in your builds

prime vapor
#

Oh, those only work in development build.

#

They are basically inline tests.

glacial minnow
#

yeah but nullables are checked only at compile time so neither of them matter in terms of runtime

prime vapor
#

No nullables absolutely do matter at runtime

#

For example something is GameObject? _enemy and you want to have different logic depending on if enemy is assigned.

glacial minnow
#

the nullable context is compile-time only, no? They only produce warnings and have nothing to do with the actual runtime. I mean of course you have to use null coalescing operators

glacial minnow
#

I believe

prime vapor
#

Yes, and Assert.IsNotNull(_enemy) does not contribute to nullable analysis.

glacial minnow
#

it just cuts off all ?

prime vapor
#

Yes NRT is purely a compile time check.

glacial minnow
prime vapor
#

Null still exists at runtime

#

NRT doesn't.

glacial minnow
#

yeah okay

prime vapor
#

I think we might have a miscommunication on what you are trying to do with Assert.IsNotNull(_enemy).

glacial minnow
#

I use it to avoid having nulls where they must not be nulls

prime vapor
#

Correct, "something should not be null is actually null" is the type of error Assert.IsNotNull checks for.

#

NRT does not deal with that type of error, NRT deals with another type of error, eg "something that could be null is being used as if it couldn't be null."

glacial minnow
#

hm yeah

#

I am still confused about how to deal with it in Unity because of the issues pointed out earlier - the "fake" nulls and serialization

prime vapor
#

From what I've seen, most projects that do use NRT just does = null! for things that must be assigned in inspector and move on.

#

The problem then becomes "how I do prevent forgetting to assign something that must be assigned" and that's usually done by an editor script that checks all the scenes/prefabs, or by just play testing the game thoroughly.

glacial minnow
#

Wow never saw that null!, seems like a nice feature

glacial minnow
prime vapor
#

Personally I think NRT is always worth turning on, but some people consider the soundness of NRT is already compromised in Unity projects due to the serialization issue above, and they argue that NRT gives a false sense of security and makes you not write null checks where you should. I personally don't agree with that, but eh it's also not too unreasonable of a position.

glacial minnow
#

I see. I would expect a safety feature to be a bit more reliable but if not raising expectations and writing less secure code then it may be helpful

#

I guess I will give it a try

#

I also like that it has nothing to do with runtime and that it's easily combinable with non-nullable context. I don't really know if it makes it useful at all but I will see that

#

Thank you very much!

#

oh and I guess you also use attributes like [NotNullWhen]? Without that it kind of loses sense to use nullables if static analyzer can't figure out your code

prime vapor
#

Yes, you would have to use those NRT attributes to help nullable analysis. A common case is when you are implementing your own try methods.

fossil dagger
#

I guess I'll ask this here since this is on topic, so I'm trying to enable Nullable in Unity for the first time, and it work for other than incredibly annoyingly, Visual Studio Code seems to have a special Nullable anaylsis for Unity, but Unity only follows the standard Nullable analysis.

Basically what's special is that this outputs no nullable warnings in vscode:

#nullable enable
using UnityEngine;

public class Example : MonoBehaviour
{
    string foo;

    void FooBar()
    {
        var x = foo.Length;
    }
}

But as soon as I make it not inherit MonoBehaviour, the normal Nullable analysis applies:

public class Example
{
    // Non-nullable field 'foo' must contain a non-null value when exiting constructor.
    // Consider adding the 'required' modifier or declaring the field as nullable.
    string foo;

    void FooBar()
    {
        var x = foo.Length;
    }
}

So, is there any way to make either VSCode not use special nullable analysis for Unity, or make Unity follow this nullable analysis?

I have enabled Nullable in Unity with a file named csc.rsp which contains this:
-nullable:enable

prime vapor
#

That shouldn't be the case, I use VS Code for Unity and NRT works fine.

#

But in general you probably should use something else other than #nullable enable or else you are going to have to slap that onto every file.

#

I personally use a Directory.Build.props file with:

<Project>
  <PropertyGroup>
    <Nullable>enable</Nullable>
  </PropertyGroup>
</Project>
prime vapor
fossil dagger
prime vapor
#

Does the same also happen in VS, or only in VS Code?

#

I don't have VS installed anymore to check.

fossil dagger
#

I can't try VS since I'm on Linux

prime vapor
#

Ah.

fossil dagger
#

I could try Rider though but haven't bothered to set up an account

fossil dagger
# prime vapor I just double checked and oh wow you are right, I could've sworn it used to work...

but yeah about it being a regression, the way the changes are seems very deliberate because in my experience with Nullable and Unity (making assemblies outside of Unity) Nullable has been really awkward with MonoBehaviours, so I think the changes are to make it less awkward by making Nullable less strict with them. But it only exists in the analyzers in vscode (I don't know where the analyzers specifically come from)

#

some Nullable functionality works as expected normal, but basically initializing members is just not strict at all

prime vapor
#

Yeah I was also thinking it's from the analyzer, and IIRC VS also uses the same analyzer, so checking if that's also the case in VS would be a way to confirm that.

fossil dagger
#

true

prime vapor
#

Seems like this is the one and it's been merged since 5 years ago. I'm surprised I've never noticed it until now.

fossil dagger
#

huh. I wonder if there's a way to make Unity follow the same logic then? Since otherwise I don't see the point as it'd only really be a downgrade because Unity Editor will then just shout at me for Nullable warnings I can't see

prime vapor
#

I haven't tried it, but I think you can include this analyzer into the Unity project, so that Unity's compiler will also get those suppressions applied.

fossil dagger
#

oh, that would be nice

fossil dagger
#

I didn't try including the analyzer into the Unity project yet since I don't want to use NuGet like a caveman, but ignoring this in csc.rsp mostly solves the issue:
-nullable:enable -nowarn:CS8618

CS8618 - Non-nullable variable must contain a non-null value when exiting constructor.

#

also a bit insane if Unity doesn't use the analyzers it pushes on developers ngl

fossil dagger
#

okay I tried the Unity analyzer thing following this https://docs.unity3d.com/6000.0/Documentation/Manual/install-existing-analyzer.html and it didn't suppress the warnings. Not sure if I did something wrong though:

Any scripts that are within the same assembly definition as an analyzer are in that analyzer’s scope.
I don't know what the analyzer here would be. The assembly I put in Unity? If yes, I did it correctly probably, otherwise I didn't.

fossil dagger
prime vapor