You can’t send gtk or modify them from other threads directly. Usually you can offload the work you need to do to a different thread by decoupling it from the widgets and then sending the result over to the main thread through a channel. If what you want to do is populate a listmodel or treestore, instead of doing it using other threads you could split the work and do it asynchronously.
This blogpost is a good resource but will need to be adapted for rust.
To add to what @alatiera said, for channels, you can either use futures channels (the GLib mainloop is a futures executor) or the glib::MainContext::channel(). For the latter, see this for some examples/background.
You would have to share something (at least a bool) between your main thread and the background thread so that the background thread can periodically check if it should continue its tasks or should cancel it.
Another option instead of explicitly using a background thread would be to make use of the glib::ThreadPool. Its push_future() function allows to push new tasks to the pool and retrieving the results via a Future.
I used crossbeam_chanell to add support for multi producer, multi consumer chanell(crossbeam_channel - Rust).
I just create a new thread and put to it cloned receiver, and after processing every single file I check if something come to chanell, if yes I set dirty flag.
At the end I send with glib::MainContext::channel() command to check and draw results