Table key can not be msg.url() - sort of? (SOLVED)

I was trying to make a module that handled both go and gui objects, and needed to store references to each object. Normally I’d use go.get_id() but this is of course not available for gui scripts, so figured I would use msg.url() instead. I’m getting some strange behaviour and just wanted to check it’s as expected. I’m sure I’ll find another way to do this.

Code:

local test = {}
test["a"] = "string key"
test[go.get_id()] = "go.id key"
test[msg.url()] = "msg url key"

print(test["a"])
print(test[go.get_id()])
print(test[msg.url()])

pprint(test)

Output:

DEBUG:SCRIPT: string key
DEBUG:SCRIPT: go.id key
DEBUG:SCRIPT: nil

DEBUG:SCRIPT: 
{ --[[000002640697CB50]]
  hash: [/blob] = "go.id key",
  a = "string key",
  url: [galaxy:/blob#blob] = "msg url key"
}

The above illustrates that you can set a table key as a url (because the data is visible in a pprint), but you can’t directly access the value by specifying the same url as a key.

Any thoughts?

An msg.url() is internally a what’s called a lightuserdata.
Among other things, it means that when it’s used as a “key”, the address of that lightuserdata is used. If you create two different instances, they’ll get different addresses.

That’s why your last line ( print(test[msg.url()])) won’t work as intended.

We have an issue to create a “key” from a url (e.g. something like url.to_key() which will give a single deterministic value based on the actual url values):

4 Likes

That makes perfect sense, thanks!

local urlkey = msg.url()
local test = {}
test[urlkey] = "msg url key"
print(test[urlkey])


DEBUG:SCRIPT: msg url key

:+1:

1 Like

There are workarounds, usually involving the hash_to_hex() function: (untested code)

local str =  hash_to_hex(url.socket or hash("")) .. hash_to_hex(url.path) .. hash_to_hex(url.fragment or hash(""))
local key = hash(str)
1 Like

Related (and battle tested function):

2 Likes

Thanks @totebo and @Mathias_Westerdahl !

I was actually going down the same line myself, but without your intervention I would have come up with something like:

local urlkey = hash(url.socket .. url.path .. url.fragment)

That seems to work in my simple example.

So the key differences to your solutions are:

  • not catching instances where socket (or path or fragment) aren’t available - presumably causing errors in some situations (e.g. where any of the values are nil the string concatenation would fail)
  • not using hash_to_hex()

I’m quite on board with the first point (though I can’t imagine when it would happen) but I don’t understand the second one (not familiar with hash_to_hex() at all). Is it because string concatenations are slow, and using hash_to_hex() is faster?

I think we return a numerical representation of the hashed value in release builds.

2 Likes