diff --git a/content/learn/course/advanced/exceptions/noexcept.mdx b/content/learn/course/advanced/exceptions/noexcept.mdx
index 8d8de23a4..336ea8523 100644
--- a/content/learn/course/advanced/exceptions/noexcept.mdx
+++ b/content/learn/course/advanced/exceptions/noexcept.mdx
@@ -10,5 +10,202 @@ import NotFinished from "@site/i18n/en/presets/NotFinished.mdx";
-# Using `noexcept` specifier
+# noexcept
+Exception handling is a fundamental concept in nearly all contemporary programming languages, and C++ is no different in this respect.
+However, one crucial aspect we have yet to explore is the noexcept operator and specifier.
+This valuable tool allows us to precisely indicate whether a function is intended to throw exceptions.
+In this lesson, we will delve into a comprehensive understanding of how these utilities are used
+
+## Motivation
+This utility allows to express intent clearly: whether a function throws or not, or whether there are specific cases in which
+it may throw. This is important in programming in general, and holds true for C++ as well: anyone reading your code
+can instantly tell whether a function throws or not, and furthermore, using noexcept correctly may allow optimizations to be made,
+for example by standard library code.
+A good example of optimization is the reallocations made by `std::vector`. Let's suppose we create our own class `Foo`.
+We'd like to store our type `Foo` inside `std::vector`, such as `std::vector`. For the sake of the example, let's also assume
+that our vector will reallocate memory at a certain time.
+If, for some reason, we do not mark our move constructor `noexcept`, then `std::vector` will use copy construction while
+reallocating! On the contrary, had we used the `noexcept` for our move constructor, it would have used move construction to do the
+same operation. This is because `std::vector` (as most stdlib containers) must preserve the strong exception guarantee (which we
+aren't covering in this lesson). To do that, what `std::vector` does while reallocating is:
+```cpp
+// std::is_nothrow_move_constructible_v checks if Foo's move constructor is noexcept
+if constexpr (std::is_nothrow_move_constructible_v) {
+ // Move the original elements from the old buffer into the new buffer, which has more space
+ // Faster
+} else {
+ // Copy the original elements from the old buffer into the new buffer, which has more space
+ // Pessimization
+}
+```
+Of course, you can learn the details about this here.
+
+## Introduction
+In C++, the `noexcept` keyword is both an operator and a specifier. Therefore we are going to cover them both, since there are cases
+in which they might be used together.
+
+# `noexcept` as a specifier
+The `noexcept` specifier is used with functions (or constructors, or lambdas) to specify whether they throw an exception.
+```cpp
+void non_throwing_function() noexcept {
+ // Note the noexcept specifier...
+}
+
+void function_that_might_throw() {
+ // Note that we did not use noexcept here.
+}
+```
+This is the basic syntax of `noexcept`. It's almost always the last thing that you see before the function's body.
+As stated above, though, we are allowed to use this specifier within our constructors or even lambdas: let's see two examples.
+```cpp
+struct Foo {
+ Foo() = default;
+ Foo(Foo&& other) noexcept {
+ // move contents...
+ }
+};
+```
+```cpp
+int main() {
+ auto non_throwable_lambda = []() noexcept {
+ // this lambda won't throw exceptions
+ };
+}
+```
+:::note Notes
+`noexcept` actually takes in an expression, such as `noexcept(expression)`.
+However, in this case, `noexcept` without any expression implicitly means `noexcept(true)`, and it is a common practice
+to avoid writing `true` as the expression, which we are also doing in this lesson.
+`noexcept` (and thus `noexcept(true)`) implies that the function does not throw.
+:::
+The `noexcept` specifier can also take an expression and **evaluate** it: if the expression evaluates to true, then the function is declared not to throw anything.
+This is usually done with type traits (and concepts since C++20), as shown in the following example:
+```cpp
+#include
+
+template
+void foo() noexcept(std::is_nothrow_constructible::value) {
+ // this function is non throwable only if T's defualt constructor is marked noexcept
+ // basically, if std::is_nothrow_constructible::value is true, this results in noexcept(true)
+ // which means that the function it marked noexcept and it does not throw
+ // otherwise, it results in noexcept(false), which means that this function is not noexcept and it might throw
+}
+```
+However, we could also use the `noexcept` operator (as shown below) to do the same.
+
+# `noexcept` as an operator
+Although the syntax is equivalent (`noexcept(expression)`), the `noexcept` operator has a different meaning compared to its specifier counterpart.
+When you use it as an operator, it simply performs a compile time check: it returns `true` only if `expression` is declared to
+not throw any exception. It can be used inside a function's `noexcept` specifier in order to state that the function itself might throw for certain types but not for others, as we'll see in a moment.
+Sounds simple enough, right? But don't be fooled...
+Let's look at an example. Can you guess what the following statement prints?
+```cpp
+#include
+int main() {
+ std::cout << std::boolalpha << noexcept(4 + 5 == 0);
+}
+```
+
+It turns out that this code prints `true`. You might be confused, since `4 + 5 == 0` is clearly not true.
+However, note that the `noexcept` operator **only** returns `true` if the given expression is *declared not to throw*: and since a simple integer addition
+and an integer comparision can never throw, the correct result is that `noexcept(4 + 5 == 0)` returns true!
+Note that this also implies that the expression inside a `noexcept` operator is never evaluated. Whether `4 + 5 == 0` is actually true does not
+matter to this operator: all it matters is whether this expression can ever throw!
+:::caution Be aware!
+Unlike its specifier counterpart, the `noexcept` operator **never** evaluates its expression. It only checks whether the expression throws!
+For example, `noexcept(false)` still returns `true`. That is because the expression `false` is guaranteed to never throw.
+:::
+
+A final example on the usage of this operator follows.
+```cpp
+void potentially_throwing() {}
+void not_throwing() noexcept {} // this is a noexcept specifier, not a noexcept operator!
+
+struct Foo {
+};
+
+int main() {
+ Foo f;
+ // The following are all noexcept operators, and not specifiers.
+ std::cout << noexcept(potentially_throwing()); // false because our function is not marked noexcept and thus may throw
+ std::cout << noexcept(not_throwing()); // true because our function is marked noexcept and thus will not throw
+ std::cout << noexcept(Foo{}); // default constructor -> true (which means it's noexcept by default)
+ std::cout << noexcept(Foo{f}); // copy constructor -> true (which means it's noexcept by default)
+}
+```
+
+# How to differentiate `noexcept` operators and specifiers
+It might seem confusing that we use the same keyword both as a specifier and an operator. However, in practice, it is really easy to differentiate the two.
+The `noexcept` specifier is always written before the function's body:
+```cpp
+void foo() noexcept { /* ... */ }
+```
+In all other cases except for one, `noexcept` is an operator.
+As stated above, there's an exception to this rule...
+
+# Combining `noexcept` specifiers and operators together
+...The exception is that we are allowed to combine both `noexcept` specifiers and operators. The end result is similar to this:
+```cpp
+template
+void foo() noexcept(noexcept(T{})) {
+ // this function is non throwable only if T's defualt constructor is marked noexcept
+ // noexcept(T{}) is the noexcept operator, and it checks whether the constructor is noexcept
+ // the outer noexcept is the noexcept specifier, and evaluates to true if the inner
+ // noexcept operator evaluates to true
+}
+```
+As explained through the comments, it's also rather easy to differentiate the specifier from the operator: the `noexcept` operator is
+always **inside** the `noexcept` specifier.
+Note that the example above can be rewritten with type traits (and concepts) as shown before, and this is what we recommend as it is more expressive:
+```cpp
+#include
+
+template
+void foo() noexcept(std::is_nothrow_constructible::value) {
+ // this is the same as before, but more expressive through type_traits
+}
+```
+:::tip Recommendation
+We suggest using type traits (or concepts after C++20) inside the `noexcept` specifier instead of its operator counterpart.
+This is because it's more expressive and often easier to read and understand at first glance.
+:::
+
+:::note Differentiating nested `noexcept`
+Whenever you encounter nested `noexcept`, remember that the inner `noexcept` is the operator and the outer `noexcept` is the specifier.
+:::
+
+# Some notes that you should be aware of
+Here's a very short list of some things that you should be aware of when working with `noexcept`.
+- First of all, if you mark a `virtual` function with the `noexcept` specifier, then all the functions in derived classes
+that override it must also have the `noexcept` specifier. Otherwise, they will not overriding the base class' `virtual` function:
+```cpp
+struct A {
+ virtual void f() noexcept {}
+};
+struct B: A {
+ void f() override {} // illegal, missing noexcept specifier
+};
+```
+- If a function that is marked `noexcept` actually throws, `std::terminate` will be called and your program will be terminated instantly.
+This also means that you may leak resources.
+- Unlike the `noexcept` operator, the `noexcept` specifier **actually evaluates** its expression.
+```cpp
+void not_throwing() noexcept(sizeof(int)==4) {
+ // sizeof(int) is actually 4 on my machine,
+ // therefore the specifier is noexpect(true) and this function is non throwable
+}
+```
+
+## When should we use `noexcept`?
+The core guidelines have a specific section for the usage of `noexcept`... Therefore, we highly recommend reading about it here.
+Here's a little tip that you can follow after you've read the general guidelines and the reasons behind them, though:
+:::tip Don't just mark all functions noexcept
+The noexcept specifier should be used when necessary, and you should not just mark all your functions noexcept.
+First, you need to really consider whether the function can potentially throw something. It's really easy to mark a function noexcept,
+only to realize later that it actually calls some potentially-throwing functions!
+The main general guideline is:
+Mark your function noexcept *only* if it really can't throw, or it throwing is not acceptable.
+:::
+
+