Functions#
A Comprehensive Guide to Mojo programming language with Real-Life Data, Transforming Beginners into Professionals.
Mojo functions can be declared with either fn (shown above) or def (as in Python). The fn declaration enforces strongly-typed and memory-safe behaviors, while def provides Python-style dynamic behaviors. currently includes only partial support for keyword arguments, so some features such as keyword-only arguments and variadic keyword arguments (e.g. **kwargs) are not supported yet
Warning
WIP
keyword parameters support
fn foo[a: Int, b: Int = 42]():
print(a, "+", b)
foo[a=5]() # prints '5 + 42'
foo[a=7, b=13]() # prints '7 + 13'
foo[b=20, a=6]() # prints '6 + 20'
struct KwParamStruct[a: Int, msg: String = "🔥mojo🔥"]:
fn __init__(inout self):
print(msg, a)
fn use_kw_params():
KwParamStruct[a=42]() # prints '🔥mojo🔥 42'
KwParamStruct[5, msg="hello"]() # prints 'hello 5'
KwParamStruct[msg="hello", a=42]() # prints 'hello 42'
from memory.unsafe import Pointer
struct HeapArray:
var data: Pointer[Int]
var size: Int
fn __init__(inout self, size: Int, val: Int):
self.size = size
self.data = Pointer[Int].alloc(self.size)
for i in range(self.size):
self.data.store(i, val)
fn __del__(owned self):
self.data.free()
fn dump(self):
print_no_newline("[")
for i in range(self.size):
if i > 0:
print_no_newline(", ")
print_no_newline(self.data.load(i))
print("]")
var a = HeapArray(3, 1)
a.dump() # Should print [1, 1, 1]
# Uncomment to see an error:
# var b = a # ERROR: Vector doesn't implement __copyinit__
var b = HeapArray(4, 2)
b.dump() # Should print [2, 2, 2, 2]
a.dump() # Should print [1, 1, 1]
struct HeapArray:
var data: Pointer[Int]
var size: Int
fn __init__(inout self, size: Int, val: Int):
self.size = size
self.data = Pointer[Int].alloc(self.size)
for i in range(self.size):
self.data.store(i, val)
fn __copyinit__(inout self, other: Self):
self.size = other.size
self.data = Pointer[Int].alloc(self.size)
for i in range(self.size):
self.data.store(i, other.data.load(i))
fn __del__(owned self):
self.data.free()
fn dump(self):
print_no_newline("[")
for i in range(self.size):
if i > 0:
print_no_newline(", ")
print_no_newline(self.data.load(i))
print("]")
var a = HeapArray(3, 1)
a.dump() # Should print [1, 1, 1]
# This is no longer an error:
var b = a
b.dump() # Should print [1, 1, 1]
a.dump() # Should print [1, 1, 1]
# Don't worry about this code yet. It's just needed for the function below.
# It's a type so expensive to copy around so it does not have a
# __copyinit__ method.
struct SomethingBig:
var id_number: Int
var huge: HeapArray
fn __init__(inout self, id: Int):
self.huge = HeapArray(1000, 0)
self.id_number = id
# self is passed by-reference for mutation as described above.
fn set_id(inout self, number: Int):
self.id_number = number
# Arguments like self are passed as borrowed by default.
fn print_id(self): # Same as: fn print_id(borrowed self):
print(self.id_number)
fn use_something_big(borrowed a: SomethingBig, b: SomethingBig):
"""'a' and 'b' are both immutable, because 'borrowed' is the default."""
a.print_id()
b.print_id()
let a = SomethingBig(10)
let b = SomethingBig(20)
use_something_big(a, b)
struct MyInt:
var value: Int
fn __init__(inout self, v: Int):
self.value = v
fn __copyinit__(inout self, other: MyInt):
self.value = other.value
# self and rhs are both immutable in __add__.
fn __add__(self, rhs: MyInt) -> MyInt:
return MyInt(self.value + rhs.value)
# ... but this cannot work for __iadd__
# Uncomment to see the error:
#fn __iadd__(self, rhs: Int):
# self = self + rhs # ERROR: cannot assign to self!
struct MyInt:
var value: Int
fn __init__(inout self, v: Int):
self.value = v
fn __copyinit__(inout self, other: MyInt):
self.value = other.value
# self and rhs are both immutable in __add__.
fn __add__(self, rhs: MyInt) -> MyInt:
return MyInt(self.value + rhs.value)
# ... now this works:
fn __iadd__(inout self, rhs: Int):
self = self + rhs
var x: MyInt = 42
x += 1
print(x.value) # prints 43 as expected
# However...
let y = x
# Uncomment to see the error:
# y += 1 # ERROR: Cannot mutate 'let' value
fn swap(inout lhs: Int, inout rhs: Int):
let tmp = lhs
lhs = rhs
rhs = tmp
var x = 42
var y = 12
print(x, y) # Prints 42, 12
swap(x, y)
print(x, y) # Prints 12, 42
# This is not really a unique pointer, we just model its behavior here
# to serve the examples below.
struct UniquePointer:
var ptr: Int
fn __init__(inout self, ptr: Int):
self.ptr = ptr
fn __moveinit__(inout self, owned existing: Self):
self.ptr = existing.ptr
fn __del__(owned self):
self.ptr = 0
fn take_ptr(owned p: UniquePointer):
print("take_ptr")
print(p.ptr)
fn use_ptr(borrowed p: UniquePointer):
print("use_ptr")
print(p.ptr)
fn work_with_unique_ptrs():
let p = UniquePointer(100)
use_ptr(p) # Pass to borrowing function.
take_ptr(p^) # Pass ownership of the `p` value to another function.
# Uncomment to see an error:
# use_ptr(p) # ERROR: p is no longer valid here!
work_with_unique_ptrs()
def example(inout a: Int, b: Int, c):
# b and c use value semantics so they're mutable in the function
...
fn example(inout a: Int, b_in: Int, c_in: Object):
# b_in and c_in are immutable references, so we make mutable shadow copies
var b = b_in
var c = c_in
...
from python import Python
from python.object import PythonObject
var dictionary = Python.dict()
dictionary["fruit"] = "apple"
dictionary["starch"] = "potato"
var keys: PythonObject = ["fruit", "starch", "protein"]
var N: Int = keys.__len__().__index__()
print(N, "items")
for i in range(N):
if Python.is_type(dictionary.get(keys[i]), Python.none()):
print(keys[i], "is not in dictionary")
else:
print(keys[i], "is included")