Primitives are converting through casting (see Types)

Custom Types are converted through the use of Traits. Common ones being From and Into

From and Into Traits

Lets us define custom type conversions

use std::convert::From;
 
#[derive(Debug)]
struct Number {
    value: i32,
}
 
impl From<i32> for Number {
    fn from(item: i32) -> Self {
        Number { value: item }
    }
}
 
fn main() {
    let num = Number::from(30);
    println!("My number is {:?}", num);
}
use std::convert::Into;
 
#[derive(Debug)]
struct Number {
    value: i32,
}
 
impl Into<Number> for i32 {
    fn into(self) -> Number {
        Number { value: self }
    }
}
 
fn main() {
    let int = 5;
    // Try removing the type annotation
    let num: Number = int.into();
    println!("My number is {:?}", num);
}

If you have implemented From then you do not need to implement Into. They are designed to be closely connected like that.

There exist TryFrom and Tryinto for conversion traits that are fallible.

To and From String

There exist of standard libraries to help do this in an idiocratic way

use std::fmt;
 
struct Circle {
    radius: i32
}
 
impl fmt::Display for Circle {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Circle of radius {}", self.radius)
    }
}
 
fn main() {
    let circle = Circle { radius: 6 };
    println!("{}", circle.to_string());
}
use std::num::ParseIntError;
use std::str::FromStr;
 
#[derive(Debug)]
struct Circle {
    radius: i32,
}
 
impl FromStr for Circle {
    type Err = ParseIntError;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s.trim().parse() {
            Ok(num) => Ok(Circle{ radius: num }),
            Err(e) => Err(e),
        }
    }
}
 
fn main() {
    let radius = "    3 ";
    let circle: Circle = radius.parse().unwrap();
    println!("{:?}", circle);
}