struct Foo { s string mut: i int } fn (f Foo) get_s() string { return f.s } fn (f &Foo) get_s_ref() string { return f.s } fn (f Foo) add(a int) int { return a + f.i } fn (f &Foo) add_ref(a int) int { return a + f.i } fn (mut f Foo) set(a int) { f.i = a } fn (f_ Foo) set_val(a int) int { mut f := unsafe { &f_ } old := f.i f.i = a return old } fn test_methods_as_fields() { mut f := Foo{ s: 'hello' i: 1 } get_s := f.get_s get_s_ref := unsafe { f.get_s_ref } add := f.add add_ref := unsafe { f.add_ref } set := unsafe { f.set } set_val := f.set_val assert typeof(get_s).str() == 'fn () string' assert typeof(get_s_ref).str() == 'fn () string' assert typeof(add).str() == 'fn (int) int' assert typeof(add_ref).str() == 'fn (int) int' assert get_s() == 'hello' assert get_s_ref() == 'hello' assert add(2) == 3 assert add_ref(2) == 3 assert f.i == 1 set(2) assert f.i == 2 old := set_val(3) assert f.i == 2 new := set_val(5) assert old == new && old == 1 } // the difference between these two tests is that here `f` is &Foo fn test_methods_as_fields_ref() { mut f := &Foo{ s: 'hello' i: 1 } get_s := f.get_s get_s_ref := unsafe { f.get_s_ref } add := f.add add_ref := unsafe { f.add_ref } set := unsafe { f.set } set_val := f.set_val assert typeof(get_s).str() == 'fn () string' assert typeof(get_s_ref).str() == 'fn () string' assert typeof(add).str() == 'fn (int) int' assert typeof(add_ref).str() == 'fn (int) int' assert get_s() == 'hello' assert get_s_ref() == 'hello' assert add(2) == 3 assert add_ref(2) == 3 assert f.i == 1 set(2) assert f.i == 2 old := set_val(3) assert f.i == 2 new := set_val(5) assert old == new && old == 1 } struct GG_Ctx { frame_fn fn (voidptr) int } @[heap] struct App { msg string = 'hello' } fn (app &App) frame() int { return app.msg.len } fn test_ctx_arg_expected() { mut app := &App{} mut ctx := &GG_Ctx{ frame_fn: app.frame } assert typeof(ctx.frame_fn).str() == 'fn (voidptr) int' assert ctx.frame_fn(app) == 5 }