Contents of Module Script being modified from a script

I’m currently working on an inventory system and ran into a weird problem. I’m using a module script to store all the item info which looks like this:

local module = {
	["Apple"] = {
		["Name"] = "Apple",
		["Amount"] = 0,
		["Slot"] = 0,
		["Stackable"] = true,
		["Id"] = nil,
	}
}

return module

When accessing/requiring the module I’m doing this:

local InventoryFunctionsModule = require(InventoryModules.InventoryFunctions)
local function CreateItem(itemName)
		local defaultItemInfo = ItemInfoModule[itemName]
		table.insert(playerInventory, defaultItemInfo)
		defaultItemInfo.Id = HttpService:GenerateGUID(false)
end

Now what happening is that when I’m modifying the defaultItemInfo it actually changes the contents of the module:

defaultItemInfo.Id = HttpService:GenerateGUID(false)

So doing that actually changes the “Id” in the ModuleScript. I even printed it on a whole other script, and it still printed the Id instead of the default value, nil.

From my understanding, you can’t modify what’s inside a ModuleScript from another script so I’m totally confused. I even tried requiring the module inside the function and it still changed what’s inside. I might be missing something totally obvious, but I have no clue what to do.

You can’t modify the source of a ModuleScript at runtime. You can, however, modify the contents of a table it returns at runtime.

If you call require on a ModuleScript and it returns a table of some kind, changes made to this table are “visible” to any other script that requires that same table, because it is the exact same table.

Edit: Sorry, hit submit too soon…

1 Like

require caches a module’s result when it is required for the first time. So subsequent calls to require on the same module return the same table/function/whatever you returned. Even in different scripts which is a good thing. If you don’t want this behavior then you can bypass it by requiring a clone of the module.

local InventoryFunctionsModule = require(InventoryModules.InventoryFunctions:Clone())

Don’t do this! This is disastrous for memory management. Instead, your ModuleScript should return a factory function for building a new table instead.

return function ()
    return {
        -- Table stuff, of course...
    }
end

Then when you require, call the function instead:

local myTableFactory = require(ModuleScript)
local myTable = myTableFactory() -- Call the function
6 Likes

What do you mean by management? Is it memoized and it keeps the clone in memory somewhere?

This solved the problem! I never knew this I just always thought that contents inside a module script could never be changed when the game is running. I knew that whatever is returned could be modified but never knew modifying what’s returned actually changes the contents inside a module script. I also have another question about this. Will I have to delete this cloned module script for memory purposes?

  • The clone still exists in memory somewhere, even if you no longer are using the table it returned. If it had children, you will have needless copies of all of those.
  • The ModuleScript may or may not depend on its parent being consistent. It doesn’t matter here, but it can affect things in other cases.
  • The purpose of the clone is just to create multiple copies of the same table; this is something done much easier by using a function. Cloning an object is a lot less performant.

That said, it’s a great wise-guy quick fix, but it’s definitely not something you should do.

3 Likes

You are better off going with ozzypig’s suggestion to just return a factory function. and call it whenever you need a new table.

3 Likes