roblox-luau-core
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseLuau Core Language
Luau核心语言
When to Use
使用场景
Load this skill when the task involves:
- General Luau syntax, variables, operators, or control flow
- Table operations (arrays, dictionaries, iteration, manipulation)
- String patterns and string library usage
- Math operations and helpers
- Scope, closures, and variable lifetime
- Common idioms and their pitfalls
- Porting code from JavaScript/Python/other languages to Luau
- Understanding sharp edges (1-based indexing, nil semantics, truthiness)
Hand off to other skills when:
- Type annotations, generics, inference, strictness modes →
roblox-luau-types - OOP patterns, async/promises, module architecture, service patterns →
roblox-luau-patterns - Roblox engine APIs, services, networking, data storage → domain skills
roblox-*
当任务涉及以下内容时加载此技能:
- Luau通用语法、变量、运算符或控制流
- Table操作(数组、字典、迭代、处理)
- 字符串模式与字符串库使用
- 数学运算与辅助函数
- 作用域、闭包与变量生命周期
- 常见编程惯用法及其陷阱
- 将JavaScript/Python/其他语言的代码移植到Luau
- 理解注意事项(1-based索引、nil语义、真值判断)
需转交其他技能的场景:
- 类型注解、泛型、类型推断、严格模式 →
roblox-luau-types - OOP模式、异步/承诺、模块架构、服务模式 →
roblox-luau-patterns - Roblox引擎API、服务、网络、数据存储 → 领域技能
roblox-*
Decision Rules
决策规则
- Stay within pure Luau syntax, semantics, and standard library
- Treat Luau as Lua 5.1 + Luau extensions. Do NOT assume Lua 5.2+ features
- Prefer variables and
localby defaultlocal function - Keep control flow direct: /
if/elseif, loops,else,breakover clever boolean trickscontinue - Use Luau-specific syntax when it improves correctness/readability (if-expressions, compound assignment, generalized iteration)
- Prefer built-in library functions over handwritten helpers
- When unsure whether something is a language question or an engine question, answer only the pure Luau portion
- 仅涉及纯Luau语法、语义与标准库
- 将Luau视为Lua 5.1 + Luau扩展。请勿假设支持Lua 5.2+特性
- 默认优先使用变量与
locallocal function - 控制流保持直接:使用/
if/elseif、循环、else、break,而非复杂的布尔技巧continue - 当Luau特定语法能提升正确性/可读性时使用(if表达式、复合赋值、通用迭代)
- 优先使用内置库函数而非手写辅助函数
- 若不确定问题属于语言层面还是引擎层面,仅回答纯Luau相关部分
Key Rules
关键规则
- Luau is NOT Lua 5.1. Has: generics, ,
continue, string interpolation (backticks), floor division+=// - Arrays are 1-based. for length. Generalized iteration:
#tblfor k, v in tbl do - Always use (never deprecated
task.wait/spawn/delay)wait/spawn/delay - Prefer backtick interpolation over concatenation
.. - Local function order: callees above callers (no hoisting). Forward-declare for mutual recursion.
- Only and
nilare falsy.false,0, and""are truthy.{}
- Luau并非Lua 5.1。新增特性:泛型、、
continue、字符串插值(反引号)、地板除法+=// - 数组为1-based索引。使用获取长度。通用迭代:
#tblfor k, v in tbl do - 始终使用(绝不使用已废弃的
task.wait/spawn/delay)wait/spawn/delay - 优先使用反引号插值而非拼接
.. - 局部函数顺序:被调用函数需在调用函数之前声明(无变量提升)。相互递归时需提前声明
- 仅与
nil为假值。false、0与""均为真值{}
Luau Extensions (not in Lua 5.1)
Luau扩展特性(Lua 5.1中不存在)
luau
-- Compound assignment operators
score += 10
score -= 5
score *= 2
-- continue keyword (skips to next iteration)
for i = 1, 10 do
if i % 2 == 0 then continue end
print(i)
end
-- Generalized iteration (preferred over ipairs/pairs)
for index, item in items do print(index, item) end
for key, value in stats do print(key, value) endluau
-- 复合赋值运算符
score += 10
score -= 5
score *= 2
-- continue关键字(跳过当前迭代,进入下一次)
for i = 1, 10 do
if i % 2 == 0 then continue end
print(i)
end
-- 通用迭代(优先于ipairs/pairs)
for index, item in items do print(index, item) end
for key, value in stats do print(key, value) endTables
Table
Tables are the only compound data structure. They serve as arrays, dictionaries, objects, and namespaces.
luau
-- Dictionary (string keys)
-- NOTE: name = "Alice" is shorthand for ["name"] = "Alice".
-- Luau tables are NOT JSON objects. Keys are strings, not identifiers.
local player = {
name = "Alice",
health = 100,
inventory = {},
}
print(player.name) --> "Alice"
print(player["health"]) --> 100
-- Dynamic keys REQUIRE bracket notation
local fieldName = "health"
print(player[fieldName]) --> 100
-- Arrays are 1-based, NOT 0-based
local items = { "sword", "shield", "potion" }
print(items[1]) --> "sword"
print(#items) --> 3 (length operator)Table是Luau中唯一的复合数据结构,可作为数组、字典、对象与命名空间使用。
luau
-- 字典(字符串键)
-- 注意:name = "Alice"是["name"] = "Alice"的简写。
-- Luau的Table并非JSON对象。键为字符串,而非标识符。
local player = {
name = "Alice",
health = 100,
inventory = {},
}
print(player.name) --> "Alice"
print(player["health"]) --> 100
-- 动态键必须使用方括号语法
local fieldName = "health"
print(player[fieldName]) --> 100
-- 数组为1-based索引,而非0-based
local items = { "sword", "shield", "potion" }
print(items[1]) --> "sword"
print(#items) --> 3(长度运算符)Table Operations
Table操作
luau
-- table.insert: append to array
local queue = {}
table.insert(queue, "task1")
table.insert(queue, "task2")
-- queue = {"task1", "task2"}
-- table.insert at index: insert at position (shifts others right)
table.insert(queue, 1, "urgent")
-- queue = {"urgent", "task1", "task2"}
-- table.remove: remove by index (shifts others left), returns removed value
local removed = table.remove(queue, 1) --> "urgent"
-- table.remove without index removes last element
local last = table.remove(queue) --> "task2"
-- table.find: search for value in array (returns index or nil)
local fruits = { "apple", "banana", "cherry" }
local index = table.find(fruits, "banana") --> 2
local missing = table.find(fruits, "grape") --> nil
-- table.sort: in-place sort
local numbers = { 5, 3, 8, 1, 9 }
table.sort(numbers) -- ascending by default
-- numbers = {1, 3, 5, 8, 9}
-- Custom sort comparator
local players = {
{ name = "Alice", score = 150 },
{ name = "Bob", score = 200 },
{ name = "Charlie", score = 100 },
}
table.sort(players, function(a, b)
return a.score > b.score -- descending by score
end)
-- table.concat: join array elements into string
local parts = { "Hello", "world", "!" }
print(table.concat(parts, " ")) --> "Hello world !"
-- table.freeze / table.isfrozen (Luau extension - immutable tables)
local CONFIG = table.freeze({
MAX_PLAYERS = 50,
ROUND_TIME = 300,
MAP_SIZE = 500,
})
-- CONFIG.MAX_PLAYERS = 100 --> ERROR: attempt to modify a frozen table
-- table.clone (Luau extension - shallow copy)
local original = { 1, 2, 3, sub = { 4, 5 } }
local copy = table.clone(original)
copy[1] = 99
print(original[1]) --> 1 (not affected)
-- NOTE: sub-tables are still shared references (shallow copy)
-- table.move (copy elements between tables or within a table)
local src = { 10, 20, 30, 40, 50 }
local dst = {}
table.move(src, 2, 4, 1, dst) -- copy src[2..4] into dst starting at dst[1]
-- dst = {20, 30, 40}
-- table.clear (Luau extension - remove all keys, keep table reference)
local t = { 1, 2, 3 }
table.clear(t) -- t is now empty but same reference
-- Deep copy utility (not built-in - write your own)
local function deepCopy<T>(original: T): T
if typeof(original) ~= "table" then
return original
end
local copy = table.clone(original :: any)
for key, value in copy do
if typeof(value) == "table" then
copy[key] = deepCopy(value)
end
end
return copy :: T
endluau
-- table.insert:向数组末尾添加元素
local queue = {}
table.insert(queue, "task1")
table.insert(queue, "task2")
-- queue = {"task1", "task2"}
-- table.insert指定索引:在指定位置插入元素(后续元素右移)
table.insert(queue, 1, "urgent")
-- queue = {"urgent", "task1", "task2"}
-- table.remove:按索引删除元素(后续元素左移),返回被删除的值
local removed = table.remove(queue, 1) --> "urgent"
-- table.remove不指定索引时删除最后一个元素
local last = table.remove(queue) --> "task2"
-- table.find:在数组中搜索值(返回索引或nil)
local fruits = { "apple", "banana", "cherry" }
local index = table.find(fruits, "banana") --> 2
local missing = table.find(fruits, "grape") --> nil
-- table.sort:原地排序
local numbers = { 5, 3, 8, 1, 9 }
table.sort(numbers) -- 默认升序
-- numbers = {1, 3, 5, 8, 9}
-- 自定义排序比较器
local players = {
{ name = "Alice", score = 150 },
{ name = "Bob", score = 200 },
{ name = "Charlie", score = 100 },
}
table.sort(players, function(a, b)
return a.score > b.score -- 按分数降序
end)
-- table.concat:将数组元素拼接为字符串
local parts = { "Hello", "world", "!" }
print(table.concat(parts, " ")) --> "Hello world !"
-- table.freeze / table.isfrozen(Luau扩展 - 不可变Table)
local CONFIG = table.freeze({
MAX_PLAYERS = 50,
ROUND_TIME = 300,
MAP_SIZE = 500,
})
-- CONFIG.MAX_PLAYERS = 100 --> 错误:尝试修改冻结的table
-- table.clone(Luau扩展 - 浅拷贝)
local original = { 1, 2, 3, sub = { 4, 5 } }
local copy = table.clone(original)
copy[1] = 99
print(original[1]) --> 1(不受影响)
-- 注意:子table仍为共享引用(浅拷贝)
-- table.move(在table之间或table内部复制元素)
local src = { 10, 20, 30, 40, 50 }
local dst = {}
table.move(src, 2, 4, 1, dst) -- 将src[2..4]复制到dst,从dst[1]开始
-- dst = {20, 30, 40}
-- table.clear(Luau扩展 - 删除所有键,保留table引用)
local t = { 1, 2, 3 }
table.clear(t) -- t现在为空,但引用不变
-- 深拷贝工具(非内置 - 需自行实现)
local function deepCopy<T>(original: T): T
if typeof(original) ~= "table" then
return original
end
local copy = table.clone(original :: any)
for key, value in copy do
if typeof(value) == "table" then
copy[key] = deepCopy(value)
end
end
return copy :: T
endString Interpolation
字符串插值
luau
-- ALWAYS prefer backtick interpolation over .. concatenation
local name = "Alice"
local level = 42
local message = `{name} reached level {level}!`
-- Expressions in interpolation
local price = 19.99
local tax = 0.08
print(`Total: ${price * (1 + tax)}`)
-- string.split (Luau extension)
local parts = string.split("a,b,c", ",")luau
-- 始终优先使用反引号插值而非..拼接
local name = "Alice"
local level = 42
local message = `{name} reached level {level}!`
-- 插值中使用表达式
local price = 19.99
local tax = 0.08
print(`Total: ${price * (1 + tax)}`)
-- string.split(Luau扩展)
local parts = string.split("a,b,c", ",")String Patterns
字符串模式
Luau uses Lua patterns, which are NOT regular expressions. They are simpler and more limited.
luau
-- Character classes
-- %a letters %A non-letters
-- %d digits %D non-digits
-- %l lowercase %L non-lowercase
-- %u uppercase %U non-uppercase
-- %w alphanumeric %W non-alphanumeric
-- %s whitespace %S non-whitespace
-- %p punctuation %P non-punctuation
-- . any character
-- %% literal %
-- Quantifiers
-- * 0 or more (greedy)
-- + 1 or more (greedy)
-- - 0 or more (lazy)
-- ? 0 or 1
-- string.match: extract matches
local year, month, day = string.match("2026-03-04", "(%d+)-(%d+)-(%d+)")
print(year, month, day) --> "2026" "03" "04"
-- string.gmatch: iterate over all matches
local text = "score=100, level=42, health=75"
for key, value in string.gmatch(text, "(%w+)=(%d+)") do
print(key, value)
end
-- string.gsub: replace matches
local cleaned = string.gsub("Hello World", "%s+", " ")
print(cleaned) --> "Hello World"
-- Escaping pattern characters: use % before special chars
-- Special chars: ( ) . % + - * ? [ ] ^ $
local escaped = string.gsub("file.txt", "%.", "_")
print(escaped) --> "file_txt"
-- Anchors
-- ^ matches start of string
-- $ matches end of string
local isEmail = string.match("user@example.com", "^%w+@%w+%.%w+$") ~= nilLuau使用Lua模式,而非正则表达式。Lua模式更简单、功能更有限。
luau
-- 字符类
-- %a 字母 %A 非字母
-- %d 数字 %D 非数字
-- %l 小写字母 %L 非小写字母
-- %u 大写字母 %U 非大写字母
-- %w 字母数字 %W 非字母数字
-- %s 空白字符 %S 非空白字符
-- %p 标点符号 %P 非标点符号
-- . 任意字符
-- %% 字面量%
-- 量词
-- * 0次或多次(贪婪)
-- + 1次或多次(贪婪)
-- - 0次或多次(惰性)
-- ? 0次或1次
-- string.match:提取匹配内容
local year, month, day = string.match("2026-03-04", "(%d+)-(%d+)-(%d+)")
print(year, month, day) --> "2026" "03" "04"
-- string.gmatch:遍历所有匹配项
local text = "score=100, level=42, health=75"
for key, value in string.gmatch(text, "(%w+)=(%d+)") do
print(key, value)
end
-- string.gsub:替换匹配内容
local cleaned = string.gsub("Hello World", "%s+", " ")
print(cleaned) --> "Hello World"
-- 转义模式字符:在特殊字符前加%
-- 特殊字符:( ) . % + - * ? [ ] ^ $
local escaped = string.gsub("file.txt", "%.", "_")
print(escaped) --> "file_txt"
-- 锚点
-- ^ 匹配字符串开头
-- $ 匹配字符串结尾
local isEmail = string.match("user@example.com", "^%w+@%w+%.%w+$") ~= nilLuau-Specific Math Extensions
Luau特定数学扩展
luau
local intDiv = 10 // 3 --> 3 (floor division, Luau extension)
print(math.clamp(15, 0, 10)) --> 10 (Luau extension)
print(math.sign(-7)) --> -1 (Luau extension)
print(math.round(3.5)) --> 4 (Luau extension)
-- For better randomness, use Random.new()
local rng = Random.new()
print(rng:NextNumber()) --> [0, 1) float
print(rng:NextInteger(1, 100)) --> [1, 100] integerluau
local intDiv = 10 // 3 --> 3(地板除法,Luau扩展)
print(math.clamp(15, 0, 10)) --> 10(Luau扩展)
print(math.sign(-7)) --> -1(Luau扩展)
print(math.round(3.5)) --> 4(Luau扩展)
-- 为获得更好的随机性,使用Random.new()
local rng = Random.new()
print(rng:NextNumber()) --> [0, 1) 浮点数
print(rng:NextInteger(1, 100)) --> [1, 100] 整数Math Helpers
数学辅助函数
luau
-- Clamping values
local health = math.clamp(currentHealth, 0, MAX_HEALTH)
-- Linear interpolation
local function lerp(a: number, b: number, t: number): number
return a + (b - a) * t
end
-- Mapping a value from one range to another
local function map(value: number, inMin: number, inMax: number, outMin: number, outMax: number): number
return outMin + (outMax - outMin) * ((value - inMin) / (inMax - inMin))
end
-- Distance between two Vector3s
local distance = (posA - posB).Magnitude
-- Normalized direction
local direction = (target - origin).Unit
-- Rounding to decimal places
local function roundTo(value: number, places: number): number
local factor = 10 ^ places
return math.round(value * factor) / factor
end
print(roundTo(3.14159, 2)) --> 3.14luau
-- 值钳制
local health = math.clamp(currentHealth, 0, MAX_HEALTH)
-- 线性插值
local function lerp(a: number, b: number, t: number): number
return a + (b - a) * t
end
-- 将值从一个范围映射到另一个范围
local function map(value: number, inMin: number, inMax: number, outMin: number, outMax: number): number
return outMin + (outMax - outMin) * ((value - inMin) / (inMax - inMin))
end
-- 两个Vector3之间的距离
local distance = (posA - posB).Magnitude
-- 归一化方向
local direction = (target - origin).Unit
-- 四舍五入到指定小数位
local function roundTo(value: number, places: number): number
local factor = 10 ^ places
return math.round(value * factor) / factor
end
print(roundTo(3.14159, 2)) --> 3.14Common Idioms
常见编程惯用法
Ternary with and/or
使用and/or实现三元表达式
Luau has no ternary operator. Use / chains for single-value conditions:
andorluau
-- Basic ternary: condition and truthy_value or falsy_value
local status = (health > 0 and "alive" or "dead")
local label = (isAdmin and "Admin" or "User")
local color = (isActive and Color3.new(0, 1, 0) or Color3.new(1, 0, 0))
-- With function calls
local displayName = (player.DisplayName ~= "" and player.DisplayName or player.Name)
-- Nested (use sparingly - readability drops fast)
local tier = (score >= 90 and "S" or score >= 70 and "A" or score >= 50 and "B" or "C")
-- CAVEAT: if the truthy value is nil or false, the expression breaks:
-- (condition and nil or "fallback") returns "fallback" even when condition is true
-- In that case, use a proper if/else block or Luau's if-expression:
local result = if condition then valueA else valueBLuau没有三元运算符。对单值条件使用/链式表达式:
andorluau
-- 基础三元表达式:condition and 真值 or 假值
local status = (health > 0 and "alive" or "dead")
local label = (isAdmin and "Admin" or "User")
local color = (isActive and Color3.new(0, 1, 0) or Color3.new(1, 0, 0))
-- 结合函数调用
local displayName = (player.DisplayName ~= "" and player.DisplayName or player.Name)
-- 嵌套(尽量少用 - 可读性会快速下降)
local tier = (score >= 90 and "S" or score >= 70 and "A" or score >= 50 and "B" or "C")
-- 注意:如果真值为nil或false,表达式会失效:
-- (condition and nil or "fallback") 即使condition为true,也会返回"fallback"
-- 这种情况下,使用标准if/else块或Luau的if表达式:
local result = if condition then valueA else valueBSharp Edges
注意事项
1-Based Indexing
1-Based索引
Luau arrays are 1-indexed. The first element is , not .
array[1]array[0]luau
local items = { "first", "second", "third" }
print(items[1]) --> "first"
print(items[0]) --> nil (NOT an error, just nil)
-- Off-by-one errors are common when porting from other languages
for i = 1, #items do -- correct: 1 to length
print(items[i])
endLuau数组为1-based索引。第一个元素是,而非。
array[1]array[0]luau
local items = { "first", "second", "third" }
print(items[1]) --> "first"
print(items[0]) --> nil(不是错误,仅返回nil)
-- 从其他语言移植时容易出现差一错误
for i = 1, #items do -- 正确:从1到数组长度
print(items[i])
endThe #
Operator and Nil Gaps
##
运算符与nil间隙
#The (length) operator is only reliable for contiguous arrays with no nil gaps.
#luau
-- Reliable: contiguous array
local a = { 1, 2, 3, 4, 5 }
print(#a) --> 5 (correct)
-- UNRELIABLE: array with nil gap
local b = { 1, 2, nil, 4, 5 }
print(#b) --> could be 2 or 5 (undefined behavior!)
-- The length operator finds ANY valid boundary where t[n] ~= nil and t[n+1] == nil
-- With gaps, multiple boundaries exist, and the result is unpredictable
-- SAFE: if you need to handle sparse data, use a dictionary with explicit count
local sparse: { [number]: string } = {}
local count = 0
sparse[1] = "a"
count += 1
sparse[5] = "e"
count += 1
-- Use count, not #sparse#luau
-- 可靠:连续数组
local a = { 1, 2, 3, 4, 5 }
print(#a) --> 5(正确)
-- 不可靠:存在nil间隙的数组
local b = { 1, 2, nil, 4, 5 }
print(#b) --> 可能为2或5(行为未定义!)
-- 长度运算符会寻找任意有效的边界,满足t[n] ~= nil且t[n+1] == nil
-- 存在间隙时,可能有多个边界,结果不可预测
-- 安全做法:如果需要处理稀疏数据,使用带显式计数的字典
local sparse: { [number]: string } = {}
local count = 0
sparse[1] = "a"
count += 1
sparse[5] = "e"
count += 1
-- 使用count,而非#sparseNil in Tables
Table中的nil
luau
-- Setting a table value to nil REMOVES the key
local t = { a = 1, b = 2, c = 3 }
t.b = nil
-- t is now { a = 1, c = 3 } - "b" key no longer exists
-- This means you cannot store nil as a meaningful value in a table
-- Use a sentinel value instead if you need to distinguish "absent" from "nil"
local NONE = newproxy(false) -- unique sentinel
local cache = {}
cache["key"] = NONE -- means "we checked, value is absent"
-- cache["other"] is nil, meaning "we haven't checked yet"
-- nil in arrays causes gaps (see # operator issue above)
local list = { 1, 2, 3 }
list[2] = nil -- creates a gap - DO NOT DO THIS
-- Use table.remove(list, 2) instead to shift elements downluau
-- 将Table值设为nil会删除对应的键
local t = { a = 1, b = 2, c = 3 }
t.b = nil
-- t现在为{ a = 1, c = 3 } - "b"键已不存在
-- 这意味着无法在Table中存储nil作为有意义的值
-- 如果需要区分“不存在”与“nil”,请使用标记值
local NONE = newproxy(false) -- 唯一标记值
local cache = {}
cache["key"] = NONE -- 表示“已检查,值不存在”
-- cache["other"]为nil,表示“尚未检查”
-- 数组中的nil会造成间隙(见上文#运算符问题)
local list = { 1, 2, 3 }
list[2] = nil -- 创建间隙 - 请勿这样做
-- 应使用table.remove(list, 2)来让后续元素左移Equality and Type Coercion
相等性与类型转换
luau
-- Luau does NOT coerce types in comparisons (unlike JavaScript)
print(0 == "0") --> false
print(1 == true) --> false
print("" == false) --> false
-- Only nil and false are falsy
-- 0, "", and empty tables are TRUTHY
if 0 then print("0 is truthy") end --> prints
if "" then print("empty string is truthy") end --> prints
if {} then print("empty table is truthy") end --> prints
-- This means you cannot use `if value then` to check for empty strings or zero
-- Be explicit:
if value ~= nil and value ~= "" then end
if value ~= nil and value ~= 0 then endluau
-- Luau在比较时不会进行类型转换(与JavaScript不同)
print(0 == "0") --> false
print(1 == true) --> false
print("" == false) --> false
-- 仅nil与false为假值
-- 0、""与空Table均为真值
if 0 then print("0是真值") end --> 会打印
if "" then print("空字符串是真值") end --> 会打印
if {} then print("空Table是真值") end --> 会打印
-- 这意味着不能使用`if value then`来检查空字符串或0
-- 需显式检查:
if value ~= nil and value ~= "" then end
if value ~= nil and value ~= 0 then endTable Reference Semantics
Table引用语义
luau
-- Tables are passed and assigned by REFERENCE, not by value
local original = { 1, 2, 3 }
local alias = original
alias[1] = 99
print(original[1]) --> 99 (both point to the same table)
-- To get an independent copy, use table.clone (shallow) or a deep copy function
local copy = table.clone(original)
copy[1] = 0
print(original[1]) --> 99 (unaffected)
-- But nested tables are still shared in a shallow clone
local nested = { data = { 1, 2, 3 } }
local shallowCopy = table.clone(nested)
shallowCopy.data[1] = 99
print(nested.data[1]) --> 99 (shared reference!)
-- Use a deep copy for nested structuresluau
-- Table通过引用传递与赋值,而非值传递
local original = { 1, 2, 3 }
local alias = original
alias[1] = 99
print(original[1]) --> 99(两者指向同一个Table)
-- 如需独立副本,使用table.clone(浅拷贝)或深拷贝函数
local copy = table.clone(original)
copy[1] = 0
print(original[1]) --> 99(不受影响)
-- 但浅拷贝中的嵌套Table仍为共享引用
local nested = { data = { 1, 2, 3 } }
local shallowCopy = table.clone(nested)
shallowCopy.data[1] = 99
print(nested.data[1]) --> 99(共享引用!)
-- 嵌套结构需使用深拷贝Scope and Closures
作用域与闭包
luau
-- Common loop closure bug
local functions = {}
for i = 1, 5 do
functions[i] = function()
return i
end
end
-- In Luau, each loop iteration creates a new 'i' variable,
-- so this actually works correctly (unlike some other languages)
print(functions[1]()) --> 1
print(functions[5]()) --> 5
-- But watch out with while loops - the variable is shared
local fns = {}
local i = 1
while i <= 5 do
fns[i] = function()
return i
end
i += 1
end
print(fns[1]()) --> 6 (all functions share the same 'i' which is now 6)
-- Fix: capture the value in a local
local fns2 = {}
local j = 1
while j <= 5 do
local captured = j
fns2[j] = function()
return captured
end
j += 1
end
print(fns2[1]()) --> 1 (correct)luau
-- 常见循环闭包错误
local functions = {}
for i = 1, 5 do
functions[i] = function()
return i
end
end
-- 在Luau中,每次循环迭代都会创建新的'i'变量,
-- 因此这段代码实际能正常工作(与某些语言不同)
print(functions[1]()) --> 1
print(functions[5]()) --> 5
-- 但while循环需注意 - 变量是共享的
local fns = {}
local i = 1
while i <= 5 do
fns[i] = function()
return i
end
i += 1
end
print(fns[1]()) --> 6(所有函数共享同一个'i',其值现在为6)
-- 修复:在局部变量中捕获值
local fns2 = {}
local j = 1
while j <= 5 do
local captured = j
fns2[j] = function()
return captured
end
j += 1
end
print(fns2[1]()) --> 1(正确)Local Function Declaration Order
局部函数声明顺序
Luau has no hoisting - a is invisible to code above its declaration.
local functionluau
-- BAD: helperB is nil when functionA runs
local function functionA()
helperB() -- ERROR: attempt to call a nil value
end
local function helperB()
print("helper")
end
-- GOOD: callee declared before caller
local function helperB()
print("helper")
end
local function functionA()
helperB() -- works
endFor mutual recursion (A calls B, B calls A), use a forward declaration:
luau
local functionB -- forward declaration (declares variable, no assignment)
local function functionA(x: number)
if x <= 0 then return end
functionB(x - 1)
end
function functionB(x: number) -- no 'local' here (already declared above)
if x <= 0 then return end
functionA(x - 1)
endRule: Callees above callers, always. If a is called by code above its definition, that is a runtime nil-error bug.
local functionLuau没有变量提升 - 在其声明之前对代码不可见。
local functionluau
-- 错误:functionA运行时helperB为nil
local function functionA()
helperB() -- 错误:尝试调用nil值
end
local function helperB()
print("helper")
end
-- 正确:被调用函数在调用函数之前声明
local function helperB()
print("helper")
end
local function functionA()
helperB() -- 正常工作
end对于相互递归(A调用B,B调用A),需使用提前声明:
luau
local functionB -- 提前声明(声明变量,未赋值)
local function functionA(x: number)
if x <= 0 then return end
functionB(x - 1)
end
function functionB(x: number) -- 此处不使用'local'(已在上方声明)
if x <= 0 then return end
functionA(x - 1)
end规则:被调用函数始终在调用函数之前声明。如果被其声明之前的代码调用,会导致运行时nil错误。
local functionMetatables: Powerful but Error-Prone
元表:功能强大但易出错
luau
-- Common mistake: forgetting __index
local MyClass = {}
-- Missing: MyClass.__index = MyClass
function MyClass.new()
return setmetatable({}, MyClass)
end
function MyClass:doSomething()
print("doing something")
end
local obj = MyClass.new()
obj:doSomething() --> ERROR: attempt to call a nil value
-- Because __index is not set, method lookup fails
-- Common mistake: modifying the metatable instead of the instance
function MyClass:setName(name: string)
-- BAD: this sets it on the class table, shared by all instances!
MyClass.name = name
-- GOOD: set on the instance
self.name = name
endluau
-- 常见错误:忘记设置__index
local MyClass = {}
-- 缺失:MyClass.__index = MyClass
function MyClass.new()
return setmetatable({}, MyClass)
end
function MyClass:doSomething()
print("doing something")
end
local obj = MyClass.new()
obj:doSomething() --> 错误:尝试调用nil值
-- 因为未设置__index,方法查找失败
-- 常见错误:修改元表而非实例
function MyClass:setName(name: string)
-- 错误:这会将值设置在类Table上,被所有实例共享!
MyClass.name = name
-- 正确:设置在实例上
self.name = name
endReserved Keywords as Identifiers
保留关键字作为标识符
Luau reserves certain words for the language syntax. These cannot be used as identifiers:
and, break, do, else, elseif, end, false, for, function, if, in,
local, nil, not, or, repeat, return, then, true, until, while,
continue (Luau-specific)luau
-- BAD: keyword used as parameter name - syntax error
local function onComplete(return: number) end -- ERROR
local function process(continue: boolean) end -- ERROR
-- GOOD: renamed to avoid reserved keyword
local function onComplete(result: number) end
local function process(shouldContinue: boolean) endLuau保留了某些单词用于语言语法,这些单词不能用作标识符:
and, break, do, else, elseif, end, false, for, function, if, in,
local, nil, not, or, repeat, return, then, true, until, while,
continue(Luau特定)luau
-- 错误:使用关键字作为参数名 - 语法错误
local function onComplete(return: number) end -- 错误
local function process(continue: boolean) end -- 错误
-- 正确:重命名以避免保留关键字
local function onComplete(result: number) end
local function process(shouldContinue: boolean) endJS → Luau Translation Table
JavaScript → Luau转换对照表
AI models trained on JavaScript commonly generate patterns that don't exist in Luau. This table covers the most frequent mistakes.
| JavaScript | Luau | Notes |
|---|---|---|
| | No built-in map/filter/reduce on tables |
| Loop with | No built-in filter |
| Loop with early return | No built-in find |
| | Returns index or nil |
| | |
| | Removes and returns last element |
| | No splice equivalent |
| | |
| No direct equivalent - use | |
| | |
| | No spread operator |
| | No const/let/var |
| | |
| | No arrow functions |
| | No arrow functions |
| | No |
| | Not |
| | No null/undefined distinction |
| | Parentheses required |
| | |
| | Luau |
| | No optional chaining |
| Manual table copy with loop | No spread operator |
| Manual copy with loop or | No spread operator |
| Regular table | Luau tables are dictionaries by default |
| | Use table as set |
| | Same if using evaera/Promise |
| | No async/await syntax |
| | No try/catch |
| | |
| | Prototype-based OOP |
| | |
| | No ES modules |
| | Module returns its public API |
| | Use backtick interpolation, NOT |
| | Backticks are the Luau way |
基于JavaScript训练的AI模型常生成Luau中不存在的模式。本表涵盖最常见的错误。
| JavaScript | Luau | 说明 |
|---|---|---|
| | Table无内置map/filter/reduce |
| 循环配合 | 无内置filter |
| 循环配合提前返回 | 无内置find |
| | 返回索引或nil |
| | |
| | 删除并返回最后一个元素 |
| 循环调用 | 无等效splice |
| | 使用 |
| 无直接等效 - 使用 | |
| | |
| | 无扩展运算符 |
| | 无const/let/var |
| | |
| | 无箭头函数 |
| | 无箭头函数 |
| | Luau无 |
| | 不是 |
| | 无null/undefined区分 |
| Roblox类型用 | 需加括号 |
| | |
| | Luau |
| | 无可选链 |
| 循环手动复制Table | 无扩展运算符 |
| 循环手动复制或使用 | 无扩展运算符 |
| 普通Table | LuauTable默认作为字典 |
| | 使用Table作为集合 |
| | 使用evaera/Promise时语法相同 |
| | 无async/await语法 |
| | 无try/catch |
| | |
| | 基于原型的OOP |
| | |
| | 无ES模块 |
| | 模块返回其公开API |
| | 使用反引号插值,而非 |
| | 反引号是Luau的标准方式 |
Type-Specific Confusion
类型特定混淆
| JavaScript | Luau | Why AI Gets It Wrong |
|---|---|---|
| | Luau has no type coercion in |
| | Only |
| | |
| | No null/undefined split |
| | No Array type distinction |
| N/A - strings are not indexable | No string methods, use |
| JavaScript | Luau | AI出错原因 |
|---|---|---|
| | Luau |
| | 仅 |
| | Luau中 |
| | 无null/undefined区分 |
| | 无Array类型区分 |
| 不适用 - 字符串不可索引 | 无字符串方法,使用 |
Common Mistakes
常见错误
- Treating or
0as falsy — only""andfalseare falsy in Luaunil - ternary fails when
a and b or ccan beborfalsenil - Assuming zero-based arrays — Luau arrays are one-based
- Mixing and
:syntax (defining with., calling with:). - Relying on dictionary iteration order
- Missing args become , extra args are silently ignored
nil - Using globals where locals/closures fit
- Overusing metatables for simple data containers
- Assuming , bitwise operators, or integer semantics exist in Luau
goto - Using concatenation instead of backtick interpolation
..
- 将或
0视为假值 — Luau中仅""与false为假值nil - 当可能为
b或false时,nil三元表达式失效a and b or c - 假设数组为0-based索引 — Luau数组为1-based
- 混淆与
:语法(用.定义,用:调用). - 依赖字典迭代顺序
- 缺失参数变为,多余参数被静默忽略
nil - 在适合使用局部变量/闭包的场景使用全局变量
- 对简单数据容器过度使用元表
- 假设Luau支持、位运算符或整数语义
goto - 使用拼接字符串而非反引号插值
..
Quality Checklist
质量检查清单
- Valid Luau syntax (not Lua 5.2+, not JavaScript)
- All variables declared with
local - Simplest correct control-flow construct used
- Tables used intentionally (array vs dictionary vs metatable-backed)
- Iteration matches data shape (generalized )
for k, v in tbl do - Standard library preferred over custom helpers
- String building uses backtick interpolation
- No nil gaps in arrays
- Callees declared above callers
- No reserved keywords used as identifiers
- 有效的Luau语法(非Lua 5.2+,非JavaScript)
- 所有变量均使用声明
local - 使用最简单的正确控制流结构
- 有意使用Table(数组、字典、元表实现)
- 迭代方式匹配数据结构(通用)
for k, v in tbl do - 优先使用标准库而非自定义辅助函数
- 字符串构建使用反引号插值
- 数组中无nil间隙
- 被调用函数在调用函数之前声明
- 未使用保留关键字作为标识符",