---
title: Functions
description: Functions
---

import Link from '@slint/common-files/src/components/Link.astro';

Similar to other programming languages, functions in Slint are way to name, organize and reuse
a piece of logic/code.

Functions can be defined as part of a component, or as part of an element within a component.
It is not possible to declare global (top-level) functions, or to declare them as part of a
struct or enum. It is also not possible to nest functions within other functions.

## Declaring Functions

Functions in Slint are declared using the `function` keyword. For example:

```slint
export component Example {
    // ...
    function my-function(parameter: int) -> string {
        // Function code goes here
        return "result";
    }
}
```

Functions can have parameters which are declared within parentheses, following the format `name: type`.
These parameters can be referenced by their names within the function body. Parameters are passed by
value.

Functions can also return a value. The return type is specified after `->` in the function
signature. The return type is `void` if no return type is specified. The `return` keyword is used
within the function body to return an expression of the declared type. If a function expects a
return value and does not have an explicit return statement then the value of the last statement is
returned by default.

Functions can be annotated with the `pure` keyword.
This indicates that the function does not cause any side effects.
More details can be found in the <Link type="Purity" /> chapter.

## Calling Functions

A function can be called without an element name (like a function call in other languages) or with
an element name (like a method call in other languages):

```slint
import { Button, VerticalBox } from "std-widgets.slint";

export component Example {
    // Call without an element name:
    property <string> my-property: my-function();
    // Call with an element name:
    property <int> my-other-property: my_button.my-other-function();

    pure function my-function() -> string {
        return "result";
    }

    VerticalBox {
        Text {
            // Called with a pre-defined element:
            text: root.my-function();
        }

        my_button := Button {
            text: "Click me";
            clicked => { self.text = root.my-other-property; }
            pure function my-other-function() -> int {
                return 42;
            }
        }
    }
}
```



## Function Visibility

By default, functions are private and cannot be accessed from other components.

However, their accessibility can be modified using the `public` or `protected` keywords.

- A root-level function annotated with `public` can be accessed by any component.

To access such a function from a different component, you always need a target, which in practice
means the calling component must declare the called component as one of its child elements.

```slint
export component HasFunction {
    public pure function double(x: int) -> int {
        return x * 2;
    }
}

export component CallsFunction {
    property <int> test: my-friend.double(1);

    my-friend := HasFunction {
    }
}
```

If a function is declared in a child element, even if marked public, it is not possible to call it
from another component, as the child elements themselves are not public and a valid target for the
function does not exist:

```slint
export component HasFunction {
    t := Text {
        public pure function double(x: int) -> int {
            return x * 2;
        }
    }
}

export component CallsFunction {
    // Compiler error!
    // property <int> test: my-friend.t.double(1);

    my-friend := HasFunction {
    }
}
```

Functions marked `public` in an exported component can also be invoked from backend code (Rust, C++, JS).
See the language-specific documentation for the generated code to use.

- A function annotated with `protected` can only be accessed by components that directly inherit from it.

## Functions vs. Callbacks

There are a lot of similarities between functions and [callbacks](#callbacks):

- They are both callable blocks of logic/code
- They are invoked in the same way
- They can both have parameters and return values
- They can both be declared `pure`

But there are also differences:

- The code/logic in the callback can be set in the backend code and implemented in the backend language
  (Rust, C++, JS), while functions must be defined entirely in slint
- The syntax for defining a callback is different
- Callbacks can be declared without assigning a block of code to them
- Callbacks have a special syntax for declaring aliases using the two-way binding operator `<=>`
- Callback visibility is always similar to `public` functions

In general, the biggest reason to use callbacks is to be able to handle them from the backend code. Use
a function if that is not needed.

## Callbacks
Components may declare callbacks, that communicate changes of state
to the outside. Callbacks are invoked by "calling" them like you would
call a function.

You react to callback invocation by declaring a handler using the `=>` arrow syntax.
The built-in `TouchArea` element declares a `clicked` callback, that's invoked
when the user touches the rectangular area covered by the element, or clicks into
it with the mouse. In the example below, the invocation of that callback is forwarded
to another custom callback (`hello`) by declaring a handler and invoking our
custom callback:

```slint
export component Example inherits Rectangle {
    // declare a callback
    callback hello;

    area := TouchArea {
        // sets a handler with `=>`
        clicked => {
            // emit the callback
            root.hello()
        }
    }
}
```

It's possible to add parameters to a callback:

```slint
export component Example inherits Rectangle {
    // declares a callback
    callback hello(int, string);
    hello(aa, bb) => { /* ... */ }
}
```

Callbacks may also return a value:

```slint
export component Example inherits Rectangle {
    // declares a callback with a return value
    callback hello(int, int) -> int;
    hello(aa, bb) => { aa + bb }
}
```

Callback arguments can also have names.
The names of arguments have currently no semantic value, but they improve readability of your code.

```slint
export component Example inherits Rectangle {
    // Declare a callback with named argument
    callback hello(foo: int, bar: string);
    // The names can be overridden with
    // anything when setting a handler
    hello(aa, bb) => { /* ... */ }
}
```

## Aliases

It's possible to declare callback aliases in a similar way to two-way bindings:

```slint
export component Example inherits Rectangle {
    callback clicked <=> area.clicked;
    area := TouchArea {}
}
```

