let res = tokio::select! {
res = &mut t1 => { t2.abort(); t2.await; res },
res = &mut t2 => { t1.abort(); t1.await; res },
};
This will make the code wait until the cancelled task's destructor has finished running, whereas the destructor might run after you return from the function with the other code.