
Mojo v24.4 版本全解析def 参数新约定、parameter for 编译期循环与标准库 API 统一【免费下载链接】mojoThe Modular Platform (includes MAX Mojo)项目地址: https://gitcode.com/GitHub_Trending/mo/mojo本篇文章基于 Mojo 官方发布说明 v0.24.4.md发布于 2024-06-07系统梳理该版本在语言特性、标准库 API、工具链三个层面的全部变更并结合当前开源仓库中 Mojo 标准库源码位于Mojo/stdlib/std/验证底层实现。读完本文你将掌握def函数参数传递的新语义、parameter for编译期循环的正确用法、ref返回引用与 infer-only 参数等新语法的迁移要点以及String、Dict、SIMD、math等模块 API 重命名与移除的完整清单可直接用于 v24.4 前后的代码迁移与升级。版本概览本版六大核心主题v24.4 的发布主题可以概括为以下六点贯穿语言与标准库的几乎所有变更def函数的性能与易用性提升参数默认按borrowed传递仅在函数体内被修改时才生成局部拷贝消除了大量不必要的拷贝开销。围绕UnsafePointer类型持续推进标准库 API 统一指针类型的方法命名收敛为unsafe_ptr()返回值类型从DTypePointer过渡到UnsafePointer。标准库集合类型的大量易用性改进List、Dict、String等类型新增了索引、转换、查询等实用方法。Dict插入性能显著改善官方说明明确指出该指标虽仍未达到理想水平但相比此前已有大幅提升整型键的Dict受益最明显。全新的parameter for编译期循环机制取代早期可靠性不佳的unroll装饰器。Mojo 手册新增三个主题页面Control flow、Testing、使用 unsafe pointers分别对应仓库中的 control-flow.mdx 与 using-pointers.mdx。语言变更def 函数参数默认 borrowed只在被修改时拷贝这是本版最重要的语义变更。此前def函数的参数默认按owned约定处理——即调用时拷贝一份值使被调函数可以随意修改它。这在实践中带来两个问题一是因拷贝泛滥导致严重的性能开销二是非可拷贝类型必须显式声明为borrowed。v24.4 改变了策略与fn函数保持一致def函数参数默认按borrowed传递仅当参数在函数体中被修改时编译器才生成局部拷贝。这种按需拷贝策略同时改善了一致性、性能与易用性——你无需再为不可拷贝类型显式添加borrowed也不会再为从未修改的参数付出拷贝代价。隐式变量定义更灵活元组解包与全局遮蔽def函数中的隐式变量定义无需var声明即可引入变量得到增强元组返回值的隐式解包现在可以写成a, b, c foo()def return_two(i: Int) - (Int, Int): return i, i1 a, b return_two(5)可遮蔽全局不可变符号隐式变量声明现在可以遮蔽模块名、内置函数等全局不可变符号而不会报编译错误例如slice foo()新 ref 返回语法自动解引用的引用函数现在可以通过结果类型中的ref关键字返回指向存储位置的自动解引用引用value struct Pair: var first: Int var second: Int fn get_first_ref(inout self) - ref [self] Int: return self.first fn show_mutation(): var somePair Pair(5, 6) somePair.get_first_ref() 1这一机制为返回给定类型的自动解引用引用提供了通用途径直接取代了__refitem__()的存在意义。因此__refitem__()被移除改由返回引用的__getitem__()承担相应职责。infer-only 参数// 标记与依赖参数的便捷调用Mojo 新增了对infer-only 参数的支持。这类参数必须位于参数列表开头用户不能显式指定声明时位于//标记左侧与仅位置参数语法类似。这使得带有依赖参数的函数可以在调用方不书写全部参数的情况下被调用fn parameter_simd[dt: DType, //, value: Scalar[dt]](): print(value) fn call_it(): parameter_simd[Int32(42)]()上例中Int32(42)直接传给value第一个非 infer-only 参数dt则由参数自身推断为DType.int32。该机制同样适用于结构体struct ScalarContainer[dt: DType, //, value: Scalar[dt]]: pass fn foo(x: ScalarContainer[Int32(0)]): # dt 被推断为 DType.int32 pass从源码结构看这种依赖参数推断在标准库中被广泛采用。例如range()的所有重载都声明为[T: Indexer, //]形式见 range.mojo。该特性的完整规范可参考 Mojo 手册 parameters/index.mdx 中的 Infer-only parameters 小节。参数重载函数支持取引用Mojo 现在允许对参数重载的函数尚未调用时解析并形成函数引用。此前会报错现在可以正常工作fn overloaded_parameters[value: Int32](): pass fn overloaded_parameters[value: Float32](): pass fn form_reference(): alias ref overloaded_parameters[Int32()] # 现在合法deprecated 装饰器新增deprecated装饰器可用于结构体、函数、trait、别名和全局变量。被标记的声明在用户代码中被引用时会产生警告且必须提供字符串字面量形式的弃用消息deprecated(Foo is deprecated, use Bar instead) struct Foo: pass fn outdated_api(x: Foo): # warning: Foo is deprecated, use Bar instead pass deprecated(use another function!) fn bar(): pass fn techdebt(): bar() # warning: use another function!parameter for面向参数值的编译期循环v24.4 引入了parameter for这是编译期编程的新机制。它定义了一个循环其中序列以及序列中的归纳值induction values必须是参数值fn parameter_for[max: Int](): parameter for i in range(max) parameter if i 10: print(found 10!)当前限制parameter for要求序列的__iter__()返回_StridedRangeIterator即归纳变量必须是Int。官方声明未来计划放宽这些限制。在仓库源码中_StridedRange结构range.mojo正是承载此类范围迭代的核心实现其__next__()通过编译期分支分别处理浮点与整型、正向与反向迭代。迁移提示parameter for取代了此前可靠性被不断侵蚀的unroll装饰器详见移除与弃用一节。Reference 与 AnyLifetime 的 is_mutable 参数类型化Reference与AnyLifetime的is_mutable参数从底层的__mlir_type.i1值改为Bool这显著改善了显式拼写Reference类型时的可读性。Python 动态库链接策略基于 PATH 查找Mojo 现在会根据搜索路径PATH上的 Python 来链接对应的 Python 动态库。这意味着你可以激活conda等虚拟环境并直接访问该环境中安装的 Python 模块而无需再设置MOJO_PYTHON_LIBRARY。此前 Mojo 在安装时会把libpython动态库路径写入.modular/modular.cfg若激活的虚拟环境 Python 版本不同就可能产生版本冲突。安全修复AnyRegType 更名与绑定限制AnyRegType被重命名为__TypeOfAllTypes且 Mojo 现在禁止将非平凡的寄存器可传递register-passable类型绑定到__TypeOfAllTypes。官方称这封堵了语言中的一个重大安全漏洞建议泛型代码一律改用AnyType。let 关键字彻底移除let从语法层面完全消失。此前 Mojo 已移除let声明但仍保留报错信息现在该关键字已从语法grammar中彻底删除。标准库变更新增 trait 与相关特性内置 repr() 函数与 Representable trait新增内置repr()函数及Representabletrait用于获取类型的详细表示字符串。Indexer trait 与 index() 函数新增Indexertrait 用于标记实现了__index__()方法的类型。此类类型可以被常见的__getitem__()/__setitem__()实现接受也可作为新内置index()函数的参数。大多数标准库容器现在都可以用任何实现Indexer的类型来索引value struct AlwaysZero(Indexer): fn __index__(self) - Int: return 0 struct MyList: var data: List[Int] fn __init__(inout self): self.data ListInt fn __getitem__T: Indexer - Int: return self.data[index(idx)] print(MyList()[AlwaysZero()]) # prints 1实现Indexer的类型可隐式转换为Int因此你可以编写接受Int的泛型 API而无需让 API 接受泛型Indexer类型value struct AlwaysZero(Indexer): fn __index__(self) - Int: return 0 value struct Incrementer: fn __getitem__(self, idx: Int) - Int: return idx 1 var a Incrementer() print(a[AlwaysZero()]) # works and prints 1从源码看Indexertrait 要求实现__mlir_index__()方法并返回底层__mlir_type.index见 int.mojo内置index()函数则返回该值对应的Intint.mojo。range()的端点参数类型即约束为Indexer。数学函数 trait新增一批 trait让用户自定义类型可以接入内置与数学函数函数Trait必需方法abs()Absable__abs__()pow()Powable__pow__()round()Roundable__round__()math.ceilmath.Ceilable__ceil__()math.ceildivmath.CeilDivable/math.CeilDivableRaising__ceildiv__()math.floormath.Floorable__floor__()math.truncTruncable__trunc__()注意事项遵循Powabletrait 意味着该类型也可用于幂运算符**。对ceildiv()结构体可以选择遵循CeilDivable或CeilDivableRaising两者之一。由于重构仍在进行Ceilable、CeilDivable、Floorable、Truncable这几个 trait 暂未出现在 API 参考文档中。它们应从math模块导入唯一例外是Truncable临时以内置 trait 形式提供无需导入。示例——自定义复数类型接入abs()与round()from math import sqrt value struct Complex2(Absable, Roundable): var re: Float64 var im: Float64 fn __abs__(self) - Self: return Self(sqrt(self.re * self.re self.im * self.im), 0.0) fn __round__(self) - Self: return Self(round(self.re, 0), round(self.im, 0)) fn __round__(self, ndigits: Int) - Self: return Self(round(self.re, ndigits), round(self.im, ndigits))Benchmarkingbencher 模块公开benchmark包中的bencher模块正式公开并纳入文档。该模块提供Bencher类型用于执行Benchmark以及BenchmarkConfig结构体用于配置基准测试。String 与字符串类型重构本版对String系列做了大量变更其中包含一项破坏性变更破坏性变更内置类/类型的隐式String转换被移除。请显式使用str()进行转换。新增String.isspace()方法与 Python 的 universal separators 语义一致这取代了string模块中的isspace()自由函数旧函数临时以_isspace()提供参数改为UInt8其余行为不变。源码实现见 string.mojo。String.split()默认按空白分割且采用 Python 风格默认去除所有相邻空白。String.strip()/lstrip()/rstrip()现在可以去除空白以外的自定义字符并新增了针对空白、ASCII 小写/大写等的若干别名。String新增splitlines()方法支持按行边界分割兼容 universal newlines并可选保留或移除换行符。InlinedString更名为InlineString与其他类型命名保持一致。StringRef新增strip()、startswith()、endswith()方法源码见 string_span.mojo 与 string_span.mojo。新增StringSlice类型用于取代标准库代码中对不安全的StringRef的使用。StringSlice是指向编码字符串数据的非拥有型引用与StringRef不同它被安全地绑定到所指向数据的生命周期。String与StringLiteral新增as_string_slice()方法StringSlice支持从UnsafePointer加字节长度初始化。String与StringLiteral新增as_bytes_slice()方法返回字符串所拥有字节的Span。继续向UnsafePointer与无符号字节类型迁移String._as_ptr()更名为String.unsafe_ptr()返回类型改为UnsafePointer原为DTypePointerStringLiteral.data()更名为StringLiteral.unsafe_ptr()返回类型改为UnsafePointer原为DTypePointerInlineString.as_ptr()更名为unsafe_ptr()返回UnsafePointer[UInt8]原为DTypePointer[DType.int8]StringRef.data现在是UnsafePointer原为DTypePointerStringRef.unsafe_ptr()返回UnsafePointer[UInt8]原为DTypePointer[DType.int8]。其他内置功能Slice.__len__()被移除Slice不再遵循Sizedtrait切片的长度始终取决于被切片对象的长度原语义存在歧义。需要该功能的用户可使用Slice.unsafe_indices()方法它明确不做边界具体性/越界检查。新增内置sort()函数用于对元素遵循ComparableCollectionElementtrait 的列表排序。int()现在可接受字符串与指定基数来解析整数int(ff, 16)返回255。若基数指定为 0字符串将按整数字面量规则解析基数由前缀0x、0o或0b决定。新增内置bin()函数将整型转换为二进制字符串表示。新增内置atof()函数将String转换为float64。新增内置any()与all()函数用于检查集合中是否存在真值/是否全为真值。由于SIMD.__bool__()现在被约束为仅size1时可用必须显式使用这两个函数获取多元素 SIMD 向量的真值fn truthy_simd(): var vec SIMDDType.int32, 4 if any(vec): print(any elements are truthy) if all(vec): print(all elements are truthy)object现在实现全部位运算符。Tuple支持__contains__()var x Tuple(1, 2, True) if 1 in x: print(x contains 1)ListLiteral与Tuple现在只要求元素类型为Movable因此ListLiteral和Tuple自身不再Copyable。新增ImmutableStaticLifetime与MutableStaticLifetime辅助类型。源码佐证any()/all()的实现位于 bool.mojo其中包含针对SIMD的重载。UnsafePointer 与其他指针类型为UnsafePointer[Scalar[_]]指针新增memcpy()重载。从UnsafePointer及其他指针类型移除get_null()方法请改用默认构造函数UnsafePointer[T]()。大量返回指针类型的函数被统一为公开 APIunsafe_ptr()。Tensor.data()更名为unsafe_ptr()返回类型仍为DTypePointer[T]。集合类型List源码见 list.mojo新增index()方法返回EqualityComparable类型元素在List中首次出现的位置var my_list ListInt print(my_list.index(3)) # prints 1支持简化语法转换为Stringmy_list.__str__()注意List尚未遵循Stringabletrait因此暂不能用str(my_list)。count()调用语法简化my_list.count(x)。支持__contains__()可与in运算符联用if x in my_list:。新增unsafe_get()用于获取元素引用而不做边界检查、对负索引不做回绕。注意此方法不安全请谨慎使用。Dict源码见 dict.mojo新增fromkeys()用指定键与值构造Dictdict.mojo。新增clear()dict.mojo。items()与values()迭代器支持reversed()。简化字符串转换my_dict.__str__()Dict尚未遵循Stringabletrait暂不能用str(my_dict)。实现get(key)与get(key, default)dict.mojo。新增临时的__get_ref(key)方法可获取字典值的Reference。新增容器类型InlineList栈分配的列表具有静态最大容量。Span用于对连续集合取切片。InlineArray可作用于仅内存memory-only类型的数组类型。与之对比已有的StaticTuple概念上是数组类型但只能用于__TypeOfAllTypes。当前仓库中对应实现为 array.mojo其中Array是编译期确定大小的固定数组类型属于 prelude 模块无需导入即可使用。os 模块新增mkdir()与rmdir()用于创建与删除目录。新增os.path.getsize()返回路径对应文件的字节大小。新增os.path.join()函数。新增tempfile模块提供gettempdir()与mkdtemp()函数。SIMD 类型新增SIMD.shuffle()的IndexListmask 版本源码见 simd.mojomask 值必须在2 * len(self)范围内位置i的结果为(self other)[permutation[i]]。SIMD.__bool__()被约束为仅当size 1时可用多元素向量请使用any()/all()。SIMD.reduce_or()与SIMD.reduce_and()现在是按位运算并支持整型源码见 simd.mojo。新增SIMD.__repr__()获取SIMD类型的详细字符串表示。math 包重构math.bit模块移动到新的顶层bit模块相关函数更名旧名称新名称ctlzcountl_zerocttzcountr_zerobit_lengthbit_widthctpoppop_countbswapbyte_swapbitreversebit_reversemath.rotate_bits_left()与math.rotate_bits_right()移到bit模块。math模块的is_power_of_2()更名为is_power_of_two()并移到bit模块。abs()、round()、min()、max()、pow()、divmod()从math移入builtin无需再显式导入。math.tgamma()更名为math.gamma()与 Python 命名一致。isfinite()、isinf()、isnan()、nan()、nextafter()、ulp()的实现迁移到新的utils.numerics模块math模块中仍继续暴露这些函数。math.gcd()现在支持负数输入与 Python 实现一致地接受可变参数整型列表并新增接受List/Span整型的重载。异步与协程Coroutine现在需要一个生命周期参数。调用 async 函数时由解析器自动设置该参数包含所有实参的生命周期以及实参的任意生命周期访问确保 async 函数对实参的捕获在协程存活期间一直保持实参存活。async 函数调用不再允许借用非平凡的寄存器可传递类型。由于 async 函数会捕获实参而寄存器可传递类型目前没有生命周期Mojo 无法正确跟踪引用存在安全隐患。为弥补该安全缺口Mojo 暂时禁止在 async 函数中把非平凡寄存器可传递类型绑定到borrowed实参。其他标准库杂项Variant与Optional的take()更名为unsafe_take()。Variant的get()被__getitem__()取代v.get[T]()应改写为v[T]。algorithm模块中的若干函数sort()、swap()、partition()成为内置函数swap()与partition()后续可能随内置sort()的重构与优化而调整位置。testing.assert_almost_equal()现在能正确处理infinity与NaNutils/numerics.mojo中新增inf函数。工具链与开发体验变更mojo package 输出目录简化命令行mojo package my-package -o my-dir其中my-package是 Mojo 包源码目录my-dir是已存在的目录现在会输出到my-dir/my-package.mojopkg。此前必须显式写全路径如-o my-dir/my-package.mojopkg。结构化诊断--diagnostic-format json多个mojo子命令新增--diagnostic-format选项可改变错误、警告等诊断信息的输出格式。指定--diagnostic-format json后诊断将以机器更易解析的结构化 JSON Lines 格式输出。支持该选项的完整子命令列表mojo build、mojo doc、mojo run、mojo package、mojo test。原先的mojo test --json选项被并入此新选项等价用法为mojo test --diagnostic-format json。注意JSON 输出格式目前不保证跨 Mojo 版本稳定。文档字符串校验选项新增--validate-doc-strings选项对无效的 doc string 输出错误而非警告。--warn-missing-doc-strings更名为--diagnose-missing-doc-strings。新增doc_private装饰器可将声明从mojo doc的输出中隐藏同时也解除该声明必须有文档的要求例如与--diagnose-missing-doc-strings联用时。调试器与语言服务器调试器用户现在可以在 O0 构建中对函数调用设置断点即使该调用已被编译器内联。Mojo Language Server 现在支持重命名局部变量。Mojo Language Server 现在会在局部变量未被使用时报告警告。移除与弃用完整迁移指南unroll 装饰器被移除unroll装饰器已被弃用并移除。它原本保证被装饰的循环会被展开否则编译器报错但这一保证随时间的推移不断被削弱——基于编译器的方法无法像 Mojo 参数系统那样稳健。此外unroll不会让循环归纳变量成为参数值限制了其用途。请改用parameter for。math 模块移除的函数以下函数从math模块移除并给出替代方案移除的函数替代方案clamp()新的SIMD.clamp()方法round_half_down()/round_half_up()用ceil()与floor()简单实现add()/sub()/mul()/div()/mod()直接使用运算符-*/%greater()/greater_equal()/less()/less_equal()/equal()/not_equal()直接使用运算符!logical_and()/logical_xor()/logical_not()直接使用运算符^~identity()/reciprocal()可平凡实现select()直接使用SIMD.select()is_even()/is_odd()用按位与1平凡实现roundeven()新的SIMD.roundeven()方法div_ceil()新的ceildiv()函数rotate_left()/rotate_right()SIMD.rotate_{left,right}()SIMD 类型与bit.rotate_bits_{left,right}()Int接受整型参数指数的math.pow()重载—align_down_residual()用align_down()平凡实现all_true()/any_true()/none_true()直接使用SIMD.reduce_and()/SIMD.reduce_or()reduce_bit_count()直接使用SIMD.reduce_bit_count()rint()/nearbyint()视情况使用round()或SIMD.roundeven()其他移除项math.polynomial中的EvaluationMethod被移除Estrin 方法不再可用该方法限于 10 次以下多项式、使用率低、性能不明确。math.polynomial.polynomial_evaluate()的默认行为不变仍为 Horner 方法。math.bit.select()与math.bit.bit_and()被移除等价功能在SIMD.select与SIMD.__and__()。math.limit模块被移除math.limit.inf()→utils.numerics.max_or_inf()math.limit.neginf()→utils.numerics.min_or_neg_inf()math.limit.max_finite()→utils.numerics.max_finite()math.limit.min_finite()→utils.numerics.min_finite()tensor.random模块被移除等价功能通过Tensor.rand()与Tensor.randn()静态方法访问。内置SIMD结构体不再遵循Indexertrait用户必须用int显式转换Scalar值。object.print()被移除。由于object现在遵循Stringabletrait请改用print(my_object)。修复列表修复自引用 variant 导致编译器崩溃的问题issue #1837。修复 LSP 在简单 trait 定义上崩溃的问题issue #2363。修复在 alias 表达式中对FloatLiteral使用//时的错误issue #1787。字典性能多项改进整型键的字典受益最明显大型字典与值较大的字典也有显著提升。修复assert_raises未包含调用位置信息的问题issue #2692。社区贡献本版特别感谢以下社区贡献者rd4com、toiletsandpaper、helehex、artemiogr97、mikowals、kernhanda、lsh、LJ-9801、YichengDWu、gabrieldemarmiesse、fknfilewalker、jayzhan211、martinvuyk、ChristopherLR、mzaks、bgreni、Brian-M-J、leandrolcampos 等。上述标准库新功能如Indexer、String系列方法、Dict新方法、SIMD增强等中相当一部分即由社区成员提交实现其代码可在Mojo/stdlib/std/对应模块源码与Mojo/stdlib/test/测试目录中进一步查看。【免费下载链接】mojoThe Modular Platform (includes MAX Mojo)项目地址: https://gitcode.com/GitHub_Trending/mo/mojo创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考