Cannot assign value of type 'State' to a variable of type 'State'

Godot Version

4.4

Question

I have the following error:
image

The parser is not happy that I’m assigning a State… to a State. This is what Player.Jump looks like:

I understand that the engine stores classes as Resources, however even when I extend the Script class, this problem remains:
image

I’m not sure if this is a bug or if there is some workaround, as this is quite unfortunate. You can see that if I use the more generic ‘Script’ class, there is no issue:

image

It seems to be specifically upset about my classes. Technically, typing the variable as a Script is fine, however I would like the auto-complete suggestions.

Why don’t you just pull out the Jump into its own file and make it a proper class_name? It’ll easily fix your problem. I honestly don’t see a good reason to store your states within the Player class anyway.

It’s actually stored as RefCounted, not Resource. Maybe that’s somehow the reason for these errors.

1 Like

you’re missing a .new() after = Player.Jump. Right now you use State like an enum, but classes are different.

1 Like

I guess your state should either be Node or a RefCounted depending on how you want to work with it.

The reason for your error is simply the try to assign a type to a variable that’s supposed to store an instance of a class.

The idea is that the stored state of the player is separate from the physics simulation. You wouldn’t actually instance Player, it’s just a class for static functions.

The script on the CharacterBody3D in the simulation calls functions from the class Player to give it behavior.

In other words, a Player is not a concrete thing… just a set of possible behaviors. Anyways, this issue forced me to think about the problem more and I think I’ve found another solution that is maybe better.

Also, I’m curious where you heard that inner classes are instances of RefCounted?

As you can see in the image, the Parser is happy to say that Player.Jump is an instance of script, which I guess does inherit from RefCounted, but wouldn’t it be upset if it wasn’t also a Script?

The type of Player.Jump does inherit from Script, and you only gave it the type, instead of an instance (via Player.Jump.new())

3 Likes

I also have a hard time looking that information up on the web, but it can be verified easily with this test:

extends Node

class MyInnerClass:
	var my_variable: int

func _ready() -> void:
	var my_inner_class_instance = MyInnerClass.new()
	print(my_inner_class_instance)
	print(my_inner_class_instance.get_class())

The output of that code is

<RefCounted#-9223372009860233829>
RefCounted

So that means that if you don’t extend anything explicitly, it will by default be extending RefCounted.

If you explicitly say it extends Resource it will actually extend whatever you say it should extend, e.g. this:

extends Node


class MyInnerClass:
	extends Resource
	var my_variable: int


func _ready() -> void:
	var my_inner_class_instance = MyInnerClass.new()
	print(my_inner_class_instance)
	print(my_inner_class_instance.get_class())

outputs

<Resource#-9223372009860233829>
Resource

However, even though it extends the Resource, it doesn’t behave like a Resource, e.g. I still can’t export it into the inspector, or create a .tres file of this type. So I’m not sure this is meant to be working this way extending other classes than the default RefCounted. If you need a Resource, just create it in a separate file.

1 Like

He is not trolling. He is correct.
What you have there is akin to
var i:int = int
By the way trying to assign a class to an instance of itself would most certainly error in Java (and probably Rust as well).

What is happening when you type it as Script is beyond me but for sure you are not getting a class type Player.Jump.
Here is a simple test:

class xtest:   # inner class named xtest
	pass
func _ready() -> void:
	var i:Script = xtest
	print(i is xtest)   # this line errors with "Expression is of type Script so it cannot be of type xtest"
2 Likes

You are 100% correct that what I wrote is technically incorrect. But it’s also disingenuous to pretend like I didn’t obviously mistype, since the entire point of what I was doing was to avoid instancing an object. Out all of the things I wrote, that guy focused in on a statement that is technically incorrect and nonetheless a moot point, and now you’re jumping on it as well like a shark drawn to blood. If that’s not trolling then I don’t know what to say.

I want a variable that stores a pointer to a script of my choosing. Not an instance of the script, but the Resource, or RefCounted, or whatever it technically is. In Rust you have crates. In Java and Godot you have classes, but in Godot they are implemented unintuitively, I believe as a Resource because I could swear the docs say as much, but someone else here swore it’s a RefCounted. In any case, when I type that variable, I expect the editor to be able to provide auto-complete suggestions.

This already exists to a limited extent within the Godot Engine. All scripts have global scopes so you can access them as long as you explicitly reference them (i.e. Player.Jump)

Is it really such a crime that I want to be able to reference them dynamically at runtime with auto-complete suggestions? I can already dynamically access them by typing them as a Script, which necessarily means I am getting the correct reference.

I discussed this with some friends in a discord group (who were considerably more charitable) and they ultimately agreed that it is probably either a bug or more likely a limitation implemented for performance reasons.

You need to calm down a little. No one is trolling or jumping on you. Every answer here was very civil until now and everyone in this thread wanted to help you solve your problem.
Unfortunately, no one actually properly understood what you’re trying to achieve, so answers were not accurate. This definitely doesn’t mean that people have ill intentions towards you.

This one sentence is already more descriptive than the rest of the thread I believe.
You can achieve that with the following syntax:

extends Node

class MyCustomType:
	static var foo: int = 1
	var bar: String = "hello"

func _ready() -> void:
	var type = MyCustomType
	print(type.foo) # "1"
	var instance = type.new()
	print(instance.bar) # "hello"

and it has auto completion as you can see here:

Is this what you need?

3 Likes

I won’t deny that I think you’re genuinely trying to be helpful and I appreciate that, but I personally don’t believe that means the same can be said for everyone else who has contributed towards this thread. I’ve been on the internet long enough to know that no question goes unpunished.

There is a reason that the ArchLinux community is openly (and if only half-jokingly) mocked. ArchLinux, btw. Neovim, btw. But you can find these kinds of people all over.

Anyway, on to the actual point of discussion, yes!

That is what I was looking for. I suppose my mistake was that if you try typing the variable declaration under ready, i.e.:

At face value, this seems like unexpected behavior. If you think deeply about it, it makes some amount of sense - the variable is pointing to either a Script, a RefCounted, or a Resource (the godot docs, the editor, and users on this thread seem to conflict on what exactly it is pointing towards), and none of those things are a MyCustomType, but at the same time… it is literally the class definition for a MyCustomType. Is it logical that when I type the reference to MyCustomType as a MyCustomType, I get an error? The only reason being because it’s not an instance? It might not be an instance, but IT IS a MyCustomType. I’m not so sure that makes sense.

I did mistakenly say that Java supports this… I went back and messed around in Eclipse because it’s been a few years and realized this was false. But it is supported in RustEdit: It is not supported in rust, but may be in Zig and Go? Which I only mention to point out that despite what some have said here, I apparently am not the only one who thinks this makes some amount of sense.

I didn’t expect that the editor would provide auto-complete suggestions if I didn’t type it because frankly, it almost never does. I usually have to cast my variables or use the ‘as’ keyword if I’m unable to type.

Anyway, I appreciate your exercising empathy and trying to understand what I was trying to communicate. Some people seem to read my words, reference their internal library for the most literal possible meaning of that word, and then use that to completely misconstrue what I’m saying, even when it flies in the face of the actual evidence (i.e. the images that I posted).

Communication is hard, but it doesn’t help when people put literally no effort into it.

1 Like

This other example using int may help illuminate

Here i is looking for an int value, a negative or positive number. But instead it’s given the type int, which isn’t a number but a type. Similarly your variable state was looking for a State value but found a Player.Jump type.

Rust does not allow this either.

struct MyCustomType{}

fn main() {
    let temp: MyCustomType = MyCustomType; // E0423 expected value, found struct `MyCustomType`

Zig (and GDScript) allow you to assign variables to types

const MyCustomType = struct {};

pub fn main() !void {
    const temp: type = MyCustomType; // allowed! assigned to a `type`
class MyCustomType:
	pass

func _ready() -> void:
	var temp: Script = MyCustomType # allowed MyCustomType is a Script (kinda)

You have noticed GDScript uses Script as it’s type type, this is glossed over in the Static Typing docs where they use a preloaded script as a type, the const Rifle being of type Script.

This is a maybe too transparent way to start meta-programming, which gets confusing enough on it’s own.

2 Likes